@cloudbase/manager-node 5.4.0 → 5.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/database/index.js +8 -5
- package/lib/function/index.js +55 -37
- package/package.json +1 -1
- package/scripts/ci/generate-changelog.js +422 -0
- package/cloudbase-manager-node/.editorconfig +0 -8
- package/cloudbase-manager-node/.eslintignore +0 -3
- package/cloudbase-manager-node/.eslintrc.js +0 -27
- package/cloudbase-manager-node/.prettierrc.js +0 -31
- package/cloudbase-manager-node/CHANGELOG.md +0 -109
- package/cloudbase-manager-node/LICENSE +0 -6
- package/cloudbase-manager-node/README.md +0 -46
- package/cloudbase-manager-node/jest.config.js +0 -21
- package/cloudbase-manager-node/package.json +0 -72
- package/cloudbase-manager-node/scripts/link.js +0 -98
- package/cloudbase-manager-node/tsconfig.json +0 -19
- package/cloudbase-manager-node/tsconfig.test.json +0 -14
package/lib/database/index.js
CHANGED
|
@@ -64,7 +64,8 @@ class DatabaseService {
|
|
|
64
64
|
}
|
|
65
65
|
async createCollection(collectionName) {
|
|
66
66
|
let { Tag } = this.getDatabaseConfig();
|
|
67
|
-
const res = await this.
|
|
67
|
+
const res = await this.dbOpService.request('CreateTable', {
|
|
68
|
+
EnvId: this.envId,
|
|
68
69
|
Tag,
|
|
69
70
|
TableName: collectionName
|
|
70
71
|
});
|
|
@@ -75,7 +76,8 @@ class DatabaseService {
|
|
|
75
76
|
const existRes = await this.checkCollectionExists(collectionName);
|
|
76
77
|
if (existRes.Exists) {
|
|
77
78
|
let { Tag } = this.getDatabaseConfig();
|
|
78
|
-
const res = await this.
|
|
79
|
+
const res = await this.dbOpService.request('DeleteTable', {
|
|
80
|
+
EnvId: this.envId,
|
|
79
81
|
Tag,
|
|
80
82
|
TableName: collectionName
|
|
81
83
|
});
|
|
@@ -87,12 +89,13 @@ class DatabaseService {
|
|
|
87
89
|
}
|
|
88
90
|
async updateCollection(collectionName, indexiesInfo) {
|
|
89
91
|
let { Tag } = this.getDatabaseConfig();
|
|
90
|
-
const res = await this.
|
|
92
|
+
const res = await this.dbOpService.request('UpdateTable', Object.assign({ EnvId: this.envId, Tag, TableName: collectionName }, indexiesInfo));
|
|
91
93
|
return res;
|
|
92
94
|
}
|
|
93
95
|
async describeCollection(collectionName) {
|
|
94
96
|
let { Tag } = this.getDatabaseConfig();
|
|
95
|
-
return this.
|
|
97
|
+
return this.dbOpService.request('DescribeTable', {
|
|
98
|
+
EnvId: this.envId,
|
|
96
99
|
Tag,
|
|
97
100
|
TableName: collectionName
|
|
98
101
|
});
|
|
@@ -109,7 +112,7 @@ class DatabaseService {
|
|
|
109
112
|
if (options.MgoOffset === undefined) {
|
|
110
113
|
options.MgoOffset = this.DEFAULT_MGO_OFFSET;
|
|
111
114
|
}
|
|
112
|
-
const res = await this.
|
|
115
|
+
const res = await this.dbOpService.request('ListTables', Object.assign({ EnvId: this.envId, Tag }, options));
|
|
113
116
|
if (res.Tables === null) {
|
|
114
117
|
// 无集合
|
|
115
118
|
res.Collections = [];
|
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/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()
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
extends: ['alloy', 'alloy/typescript'],
|
|
3
|
-
rules: {
|
|
4
|
-
indent: ['error', 4],
|
|
5
|
-
semi: ['error', 'never'],
|
|
6
|
-
complexity: ['error', { max: 40 }],
|
|
7
|
-
'no-useless-constructor': 'off',
|
|
8
|
-
'@typescript-eslint/explicit-member-accessibility': 'off',
|
|
9
|
-
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
10
|
-
'@typescript-eslint/interface-name-prefix': 'off',
|
|
11
|
-
'@typescript-eslint/no-useless-constructor': 'off',
|
|
12
|
-
'@typescript-eslint/no-duplicate-imports': 'off'
|
|
13
|
-
},
|
|
14
|
-
env: {
|
|
15
|
-
es6: true,
|
|
16
|
-
node: true,
|
|
17
|
-
jest: true
|
|
18
|
-
},
|
|
19
|
-
overrides: [
|
|
20
|
-
{
|
|
21
|
-
files: ['*.js'],
|
|
22
|
-
rules: {
|
|
23
|
-
'@typescript-eslint/no-var-requires': 'off'
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
// 一行最多 100 字符
|
|
3
|
-
printWidth: 100,
|
|
4
|
-
// 使用 4 个空格缩进
|
|
5
|
-
tabWidth: 4,
|
|
6
|
-
// 不使用缩进符,而使用空格
|
|
7
|
-
useTabs: false,
|
|
8
|
-
// 行尾需要有分号
|
|
9
|
-
semi: false,
|
|
10
|
-
// 使用单引号
|
|
11
|
-
singleQuote: true,
|
|
12
|
-
// 对象的 key 仅在必要时用引号
|
|
13
|
-
quoteProps: 'as-needed',
|
|
14
|
-
// 末尾不需要逗号
|
|
15
|
-
trailingComma: 'none',
|
|
16
|
-
// 大括号内的首尾需要空格
|
|
17
|
-
bracketSpacing: true,
|
|
18
|
-
// 每个文件格式化的范围是文件的全部内容
|
|
19
|
-
rangeStart: 0,
|
|
20
|
-
rangeEnd: Infinity,
|
|
21
|
-
// 不需要写文件开头的 @prettier
|
|
22
|
-
requirePragma: false,
|
|
23
|
-
// 不需要自动在文件开头插入 @prettier
|
|
24
|
-
insertPragma: false,
|
|
25
|
-
// 使用默认的折行标准
|
|
26
|
-
proseWrap: 'preserve',
|
|
27
|
-
// 换行符使用 lf
|
|
28
|
-
endOfLine: 'lf',
|
|
29
|
-
// 箭头括号
|
|
30
|
-
arrowParens: 'avoid'
|
|
31
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# 4.0.0
|
|
2
|
-
- 升级 https-proxy-agent 3->5 修复 https://github.com/TencentCloud/tencentcloud-sdk-nodejs/issues/47
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# v3.9.2-beta
|
|
6
|
-
- 扩展禁用用户接口为变更用户状态接口
|
|
7
|
-
- 增加修改用户信息接口
|
|
8
|
-
|
|
9
|
-
# v3.5.0
|
|
10
|
-
|
|
11
|
-
- 新增获取云开发用户列表接口
|
|
12
|
-
- 新增停用云开发用户接口
|
|
13
|
-
- 新增批量删除云开发用户接口
|
|
14
|
-
|
|
15
|
-
# v3.4.0
|
|
16
|
-
|
|
17
|
-
- 增加云接入服务
|
|
18
|
-
- 创建函数支持同时创建云接入
|
|
19
|
-
- 优化请求错误栈
|
|
20
|
-
|
|
21
|
-
# v3.3.2
|
|
22
|
-
|
|
23
|
-
- 函数轮询状态时间修复 30s => 300s
|
|
24
|
-
|
|
25
|
-
# v3.3.0
|
|
26
|
-
|
|
27
|
-
- 静态托管支持配置重定向规则
|
|
28
|
-
- 新增删除静态托管自定义域名接口
|
|
29
|
-
- 新增获取静态网站配置接口
|
|
30
|
-
- 新增获取域名配置接口
|
|
31
|
-
- 新增域名配置接口
|
|
32
|
-
|
|
33
|
-
# v3.2.0
|
|
34
|
-
|
|
35
|
-
- 新增静态托管查询文件,配置文档,绑定自定义域名接口
|
|
36
|
-
|
|
37
|
-
# v3.1.1
|
|
38
|
-
|
|
39
|
-
- 优化文件夹删除接口
|
|
40
|
-
- 增加批量上传文件接口
|
|
41
|
-
- 修复静态网站上传单个文件没有回调的问题
|
|
42
|
-
|
|
43
|
-
# v3.0.0
|
|
44
|
-
|
|
45
|
-
- [fix] common 方法改造
|
|
46
|
-
- [add] 支持数据库 CRUD
|
|
47
|
-
|
|
48
|
-
# v2.4.1
|
|
49
|
-
|
|
50
|
-
- [fix] 修复并行调用 createFunction 接口报错的问题
|
|
51
|
-
- [add] 支持通过指定函数路径创建函数
|
|
52
|
-
|
|
53
|
-
# v2.4.0
|
|
54
|
-
|
|
55
|
-
- [add] 新增安全来源 API
|
|
56
|
-
- [fix] 修复云函数环境下取环境变量 bug (入口函数外部调用,未取到报错)
|
|
57
|
-
|
|
58
|
-
# v2.3.4
|
|
59
|
-
|
|
60
|
-
- [fix] 修复函数配置更新问题
|
|
61
|
-
|
|
62
|
-
# v2.3.2
|
|
63
|
-
|
|
64
|
-
- [fix] 修复更新函数 VPC 配置导致无法访问公网的问题
|
|
65
|
-
|
|
66
|
-
# v2.3.1
|
|
67
|
-
|
|
68
|
-
- [fix] 修复创建函数、更新函数代码,代码保护参数遗漏的问题
|
|
69
|
-
|
|
70
|
-
# v2.3.0
|
|
71
|
-
|
|
72
|
-
- [add] 支持文件层
|
|
73
|
-
- [add] 支持查询修改存储安全规则异步任务状态
|
|
74
|
-
- [fix] 文档订正
|
|
75
|
-
|
|
76
|
-
# v2.2.1
|
|
77
|
-
|
|
78
|
-
- [fix] 修复查询所有集合信息接口,集合为空时报错
|
|
79
|
-
- [fix] updateFunctionCode 支持自动安装依赖
|
|
80
|
-
|
|
81
|
-
# v2.2.0
|
|
82
|
-
|
|
83
|
-
- [fix] 修复上传文件夹等接口 `onFileFinish` 参数 TS 定义错误
|
|
84
|
-
- [add] 创建云函数返回 RequestId 响应值
|
|
85
|
-
- [add] 支持 L5 选项
|
|
86
|
-
|
|
87
|
-
# v2.1.0
|
|
88
|
-
|
|
89
|
-
- [add] 支持上传或更新函数时 等待依赖安装完成
|
|
90
|
-
- [add] 支持增量上传函数代码
|
|
91
|
-
- [add] 新增创建自定义登录私钥
|
|
92
|
-
- [add] 新增 HTTP Service API
|
|
93
|
-
- [add] 新增安全规则 API
|
|
94
|
-
|
|
95
|
-
# v2.0.0
|
|
96
|
-
|
|
97
|
-
- [refactor] 重构 storage , function 接口(breaking change)
|
|
98
|
-
- [add] 支持闭环完成环境创建
|
|
99
|
-
- [add] 支持函数操作代码保护
|
|
100
|
-
- [fix] 修复 windows 下上传文件路径 bug
|
|
101
|
-
|
|
102
|
-
# v1.3.0
|
|
103
|
-
|
|
104
|
-
- [add] 支持获取云函数代码下载链接
|
|
105
|
-
|
|
106
|
-
# v1.2.0
|
|
107
|
-
|
|
108
|
-
- [deprecated] env 创建环境功能暂时停用
|
|
109
|
-
- [add] 支持创建云函数自动安装依赖
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
ISC License (ISC)
|
|
2
|
-
Copyright <2019> <Tencent Cloud Base>
|
|
3
|
-
|
|
4
|
-
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
5
|
-
|
|
6
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# cloudbase-manager-node
|
|
2
|
-
|
|
3
|
-
云开发 manager-node 支持开发者通过接口形式对云开发提供的云函数、数据库、文件存储等资源进行创建、管理、配置等操作。更多源码内容请参见 [cloudbase-manager-node](https://github.com/TencentCloudBase/cloudbase-manager-node)。
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
npm
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @cloudbase/manager-node
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
yarn
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
yarn add @cloudbase/manager-node
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## 使用
|
|
20
|
-
|
|
21
|
-
要在你的代码内使用该模块:
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
const CloudBase = require('@cloudbase/manager-node')
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
或
|
|
28
|
-
|
|
29
|
-
```js
|
|
30
|
-
import CloudBase from '@cloudbase/manager-node'
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
初始化
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
const app = CloudBase.init({
|
|
37
|
-
secretId: 'Your SecretId',
|
|
38
|
-
secretKey: 'Your SecretKey',
|
|
39
|
-
token: 'Your SecretToken', // 使用临时凭证需要此字段
|
|
40
|
-
envId: 'Your envId' // 云环境 ID,可在腾讯云-云开发控制台获取
|
|
41
|
-
})
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## 文档
|
|
45
|
-
|
|
46
|
-
请访问新的[文档站点](https://docs.cloudbase.net/api-reference/manager/node/introduction.html)查看详细的文档。
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
roots: ['<rootDir>/test'],
|
|
3
|
-
globals: {
|
|
4
|
-
'ts-jest': {
|
|
5
|
-
tsConfig: 'tsconfig.test.json'
|
|
6
|
-
}
|
|
7
|
-
},
|
|
8
|
-
transform: {
|
|
9
|
-
'^.+\\.ts?$': 'ts-jest'
|
|
10
|
-
},
|
|
11
|
-
transformIgnorePatterns: ['node_modules'],
|
|
12
|
-
testEnvironment: 'node',
|
|
13
|
-
testPathIgnorePatterns: [
|
|
14
|
-
// PG 集成测试 / smoke 测试需要 PostgreSQL 环境,流水线默认跳过
|
|
15
|
-
'/pg-storage-integration\\.test\\.ts$/'
|
|
16
|
-
],
|
|
17
|
-
// https://github.com/facebook/jest/issues/5164
|
|
18
|
-
globalSetup: './test/global-setup-hook.ts',
|
|
19
|
-
// globalTeardown: './test/global-teardown-hook.js',
|
|
20
|
-
coverageReporters: ['json', 'lcov', 'clover', 'text-summary']
|
|
21
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@cloudbase/manager-node",
|
|
3
|
-
"version": "5.4.0",
|
|
4
|
-
"description": "The node manage service api for cloudbase.",
|
|
5
|
-
"main": "lib/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "rimraf lib types && npx tsc",
|
|
8
|
-
"test:coverage": "jest --runInBand --detectOpenHandles --coverage --testTimeout=100000",
|
|
9
|
-
"test": "jest --runInBand --detectOpenHandles --testTimeout=100000",
|
|
10
|
-
"lint": "eslint \"./**/*.ts\"",
|
|
11
|
-
"lint:fix": "eslint --fix \"./**/*.ts\"",
|
|
12
|
-
"prepublishOnly": "npm run build",
|
|
13
|
-
"watch": "rimraf lib types && tsc -w",
|
|
14
|
-
"link": "node scripts/link.js",
|
|
15
|
-
"unlink": "node scripts/link.js -d"
|
|
16
|
-
},
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "https://github.com/TencentCloudBase/cloudbase-manager-node.git"
|
|
20
|
-
},
|
|
21
|
-
"author": "Tencent CloudBase Team",
|
|
22
|
-
"license": "ISC",
|
|
23
|
-
"typings": "types/index.d.ts",
|
|
24
|
-
"devDependencies": {
|
|
25
|
-
"@lerna/create-symlink": "^6.4.1",
|
|
26
|
-
"@types/fs-extra": "^11.0.4",
|
|
27
|
-
"@types/jest": "^24.0.18",
|
|
28
|
-
"@types/mime-types": "^3.0.1",
|
|
29
|
-
"@types/node": "^12.7.4",
|
|
30
|
-
"@types/node-fetch": "^2.5.0",
|
|
31
|
-
"@types/unzipper": "^0.10.11",
|
|
32
|
-
"@typescript-eslint/eslint-plugin": "^3.7.1",
|
|
33
|
-
"@typescript-eslint/parser": "^3.7.1",
|
|
34
|
-
"eslint": "^7.6.0",
|
|
35
|
-
"eslint-config-alloy": "^3.7.4",
|
|
36
|
-
"husky": "^3.0.5",
|
|
37
|
-
"jest": "^24.9.0",
|
|
38
|
-
"lint-staged": "^9.2.5",
|
|
39
|
-
"rimraf": "^3.0.0",
|
|
40
|
-
"ts-jest": "^24.1.0",
|
|
41
|
-
"typescript": "^5.8.3"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@cloudbase/database": "^0.6.2",
|
|
45
|
-
"archiver": "^3.1.1",
|
|
46
|
-
"cos-nodejs-sdk-v5": "^2.14.0",
|
|
47
|
-
"del": "^5.1.0",
|
|
48
|
-
"fs-extra": "^11.3.0",
|
|
49
|
-
"https-proxy-agent": "^5.0.1",
|
|
50
|
-
"lodash": "^4.17.21",
|
|
51
|
-
"make-dir": "^3.0.0",
|
|
52
|
-
"micromatch": "^4.0.2",
|
|
53
|
-
"mime-types": "^3.0.2",
|
|
54
|
-
"node-fetch": "^2.6.0",
|
|
55
|
-
"query-string": "^6.8.3",
|
|
56
|
-
"unzipper": "^0.12.3",
|
|
57
|
-
"uuid": "^9.0.0",
|
|
58
|
-
"walkdir": "^0.4.1"
|
|
59
|
-
},
|
|
60
|
-
"husky": {
|
|
61
|
-
"hooks": {
|
|
62
|
-
"pre-commit": "npm run build && git add . && lint-staged"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"lint-staged": {
|
|
66
|
-
"*.ts": [
|
|
67
|
-
"eslint --fix",
|
|
68
|
-
"git add"
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
"packageManager": "yarn@1.22.22"
|
|
72
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tencent is pleased to support the open source community by making CloudBaseFramework - 云原生一体化部署工具 available.
|
|
3
|
-
*
|
|
4
|
-
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
|
-
*
|
|
6
|
-
* Please refer to license text included with this package for license details.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const os = require('os')
|
|
10
|
-
const fs = require('fs')
|
|
11
|
-
const fse = require('fs-extra')
|
|
12
|
-
const del = require('del')
|
|
13
|
-
const path = require('path')
|
|
14
|
-
const mkdirp = require('mkdirp')
|
|
15
|
-
const { execSync } = require('child_process')
|
|
16
|
-
const { createSymlink } = require('@lerna/create-symlink')
|
|
17
|
-
|
|
18
|
-
const globalNpmPath = execSync('npm root -g', {
|
|
19
|
-
encoding: 'utf-8'
|
|
20
|
-
}).trim()
|
|
21
|
-
// 如果不支持workspace 可以用下面写死
|
|
22
|
-
// const globalNpmPath = '/usr/local/lib/node_modules';
|
|
23
|
-
|
|
24
|
-
async function main() {
|
|
25
|
-
const unlink = process.argv?.[2] === '-d'
|
|
26
|
-
await linkCore(unlink)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function linkCore(unlink = false) {
|
|
30
|
-
const jsonString = await fs.readFileSync(path.join(__dirname, '../package.json'))
|
|
31
|
-
const { name } = JSON.parse(jsonString)
|
|
32
|
-
await link(path.join(process.cwd()), path.join(globalNpmPath, '@cloudbase/cli'), name, unlink)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isSymlink(path) {
|
|
36
|
-
const stats = fs.lstatSync(path)
|
|
37
|
-
return stats.isSymbolicLink()
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 将 global tcb cli 工具中的 framework-core link 到 packages/framework-core
|
|
41
|
-
async function link(src, dest, packageName, unlink = false) {
|
|
42
|
-
const prevCwd = process.cwd()
|
|
43
|
-
const destPlugin = path.join(dest, 'node_modules', '@cloudbase')
|
|
44
|
-
// 确保目录存在
|
|
45
|
-
mkdirp.sync(destPlugin)
|
|
46
|
-
// 切换 cwd
|
|
47
|
-
process.chdir(destPlugin)
|
|
48
|
-
|
|
49
|
-
const pathName = packageName.replace('@cloudbase/', '')
|
|
50
|
-
const backupPathName = `${pathName}_backup`
|
|
51
|
-
|
|
52
|
-
if (unlink) {
|
|
53
|
-
if (fs.existsSync(pathName) && !isSymlink(pathName)) {
|
|
54
|
-
console.log(`【失败】${pathName} 不是软链接`, process.cwd())
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (fs.existsSync(pathName) && fs.existsSync(backupPathName)) {
|
|
59
|
-
// 删除软链接
|
|
60
|
-
del.sync([pathName])
|
|
61
|
-
|
|
62
|
-
// 恢复备份
|
|
63
|
-
fse.moveSync(backupPathName, pathName)
|
|
64
|
-
|
|
65
|
-
// 删除软链接
|
|
66
|
-
console.log('【成功】删除软链接:', process.cwd())
|
|
67
|
-
} else {
|
|
68
|
-
console.info(
|
|
69
|
-
`不存在 ${pathName} 或 ${backupPathName},请重装安装 npm i @cloudbase/cli -g`,
|
|
70
|
-
process.cwd()
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
// 创建软链接
|
|
75
|
-
|
|
76
|
-
if (fs.existsSync(pathName)) {
|
|
77
|
-
if (!fs.existsSync(backupPathName)) {
|
|
78
|
-
// 不存在备份则进行备份
|
|
79
|
-
|
|
80
|
-
if (!isSymlink(pathName)) {
|
|
81
|
-
// 不是软链接才备份
|
|
82
|
-
fse.moveSync(pathName, backupPathName)
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
// 存在备份则直接删除
|
|
86
|
-
del.sync([pathName])
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
await createSymlink(src, pathName, 'junction')
|
|
91
|
-
console.log('【成功】创建软链接:', process.cwd())
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 切回源目录
|
|
95
|
-
process.chdir(prevCwd)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
main()
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compileOnSave": true,
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"target": "ES2017",
|
|
6
|
-
"moduleResolution": "node",
|
|
7
|
-
"outDir": "lib",
|
|
8
|
-
"removeComments": false,
|
|
9
|
-
"types": ["node"],
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"declarationDir": "./types",
|
|
13
|
-
"experimentalDecorators": true,
|
|
14
|
-
"newLine": "LF",
|
|
15
|
-
"skipLibCheck": true
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "test"]
|
|
19
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compileOnSave": true,
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"experimentalDecorators": true,
|
|
5
|
-
"module": "commonjs",
|
|
6
|
-
"target": "ES2017",
|
|
7
|
-
"moduleResolution": "node",
|
|
8
|
-
"outDir": "lib",
|
|
9
|
-
"types": ["node", "jest"],
|
|
10
|
-
"esModuleInterop": true
|
|
11
|
-
},
|
|
12
|
-
"include": ["src/**/*", "test/**/*"],
|
|
13
|
-
"exclude": ["node_modules"]
|
|
14
|
-
}
|