@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.
@@ -64,7 +64,8 @@ class DatabaseService {
64
64
  }
65
65
  async createCollection(collectionName) {
66
66
  let { Tag } = this.getDatabaseConfig();
67
- const res = await this.collOpService.request('CreateTable', {
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.collOpService.request('DeleteTable', {
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.collOpService.request('UpdateTable', Object.assign({ Tag, TableName: collectionName }, indexiesInfo));
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.collOpService.request('DescribeTable', {
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.collOpService.request('ListTables', Object.assign({ Tag }, options));
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 = [];
@@ -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
- if (functionExist && force) {
349
- // 1. 更新函数代码(通过 deployMode 区分镜像部署和代码部署)
350
- const codeRes = await this.updateFunctionCode({
351
- func,
352
- base64Code,
353
- functionPath,
354
- functionRootPath,
355
- codeSecret: codeSecret,
356
- deployMode: isImageDeploy ? 'image' : undefined
357
- });
358
- // 等待函数状态正常
359
- await this.waitFunctionActive(funcName, codeSecret);
360
- // 2. 更新函数配置
361
- const configRes = await this.updateFunctionConfig(func);
362
- // 等待函数状态正常
363
- await this.waitFunctionActive(funcName, codeSecret);
364
- // 3. 创建函数触发器
365
- const triggerRes = await this.retryCreateTrigger(funcName, func.triggers);
366
- // 设置路径,创建云接入路径
367
- if (func.path) {
368
- await this.createAccessPath(funcName, func.path);
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.scfService.request('ListFunctions', {
403
- Namespace: namespace,
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.scfService.request('ListFunctions', {
419
- Namespace: namespace,
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.scfService.request('ListFunctions', {
451
- Namespace: envId,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/manager-node",
3
- "version": "5.4.0",
3
+ "version": "5.5.1",
4
4
  "description": "The node manage service api for cloudbase.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -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,8 +0,0 @@
1
- root = true
2
- [*]
3
- indent_style = space
4
- indent_size = 4
5
- end_of_line = lf
6
- charset = utf-8
7
- trim_trailing_whitespace = true
8
- insert_final_newline = true
@@ -1,3 +0,0 @@
1
- lib/
2
- types/
3
- node_modules
@@ -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
- }