# 前言
这里介绍如何使用 SCM 模板创建插件
# 接入效果
# 接入流程
# 创建 TAPD 托管应用
在开放平台选择 SCM NodeJs Vue 模板插件创建应用
# 下载代码及授权验证
在应用概览中选择代码仓库并下载
安装并使用 tplugin-cli 进行 插件开发,参考【插件开发】
# 配置 SCM 服务
在 服务集成-SCM应用中配置地址和私人令牌
以工蜂为例,在账户中查看私人令牌
# 关联代码仓库
在【项目协作-项目设置-DevOps配置-代码】中即可关联代码仓库
# 扩展能力
# plugin.yaml示例
app:
modules:
scm:
adapter:
_hooks_handler:
# 处理commit提交、MR提交等SCM webhook回调数据,返回TAPD期望的格式
adapter: hook.adapter
branches:
# 获取分支信息
get: branches.get
# 创建分支
post: branches.post
commit:
# 获取单个commit信息
get: commit.get
commit_diff:
# 获取commit的变更详细信息
get: commit.diff
hook:
# 删除webhook配置
delete: hook.delete
# 修改webhook配置
put: hook.put
hooks:
# 查询webhook配置
get: hook.get
# 创建webhook配置
post: hook.post
merge_requests:
# 获取指定MR的信息
get: mr.get
# 根据分支名获取创建MR的URL
get_mr_url: mr.get_mr_url
project:
# 获取指定项目信息
get: projects.get
projects:
# 查询项目列表
get: projects.list
query_commits:
# 查询commit列表
get: commit.query
support:
# 是否支持创建分支、关联分支等操作
branch: true
# 是否支持MR相关操作
mr: true
# 是否支持关联仓库操作
repo: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 接口实现
# SCM应用hook格式
TAPD接收到第三方SCM的回调数据之后, 会检查插件是否配置了_hooks_handler处理器,如果未配置,则根据默认解析逻辑处理回调数据,如果配置了,则调用插件的_hooks_handler处理回调数据。插件中需要根据事件类型将回调数据转换为TAPD需要的格式。
# plugin.yaml定义:
scm:
adapter:
_hooks_handler:
adapter: hook.handler
1
2
3
4
2
3
4
# 转换格式
# Commit事件
{
// 事件类型
"object_kind": "push",
// 所属分支
"ref": "refs/heads/master",
// 操作触发人id(git侧)
"user_id": 1,
// 操作触发人(git侧)
"user_name": "Administrator",
// 仓库ID
"project_id": 2,
// 提交commit列表
"commits": [
{
// commit ID
"id": "8b667893734892bd2df25e99fb960c2ed13cf95d",
// commit 提交信息
"message": "Update ssss",
// commit 标题
"title": "Update ssss",
// commit 提交时间
"timestamp": "2024-11-21T06:10:28+00:00",
// commit 访问地址
"url": "http://gitlab.tiger.oa.com/root/luhao/-/commit/8b667893734892bd2df25e99fb960c2ed13cf95d",
// 提交用户信息
"author": {
// 提交人用户名
"name": "Administrator",
// 提交人邮箱
"email": "tapdom@gitlab.tiger.oa.com"
},
// 新增文件列表
"added": [],
// 修改文件列表
"modified": [
"ssss"
],
// 移除文件列表
"removed": []
}
],
"total_commits_count": 1,
// 当前操作仓库信息
"repository": {
// 仓库名
"name": "luhao",
// 仓库描述
"description": "仓库描述",
// 仓库访问地址
"homepage": "http://gitlab.tiger.oa.com/root/luhao",
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Branch事件
{
// 事件类型
"object_kind": "push",
// 对分支的操作类型
// create 创建分支
// delete 删除分支
"operation_kind": "create",
// 所属分支
"ref": "refs/heads/master",
// 操作触发人id(git侧)
"user_id": 1,
// 操作触发人(git侧)
"user_name": "Administrator",
// 仓库ID
"project_id": 2,
// 提交commit列表
"commits": [],
"total_commits_count": 0,
// 当前操作仓库信息
"repository": {
// 仓库名
"name": "luhao",
// 仓库描述
"description": "仓库描述",
// 仓库访问地址
"homepage": "http://gitlab.tiger.oa.com/root/luhao",
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# MR事件
{
// 事件类型
"object_kind": "merge_request",
"user": {
// 操作触发人id(git侧)
"id": 1,
// 操作触发人(git侧)
"name": "Administrator",
"username": "root",
"email": "tapdom@gitlab.tiger.oa.com"
},
// 仓库ID
"project_id": 2,
// merge_request事件时需要携带
"object_attributes": {
"created_at": "2024-11-22 08:40:42 +0530",
// MR描述
"description": "这是一个MR",
// MR id, 如果没有这个定义的话和iid保持一致即可
"id": 125,
// MR iid,即用户看到的MR id
"iid": 31,
// MR源分支
"source_branch": "gaowei",
// MR源仓库ID
"source_project_id": 2,
// MR目标分支
"target_branch": "master",
// MR目标仓库ID
"target_project_id": 2,
// MR标题
"title": "MR标题",
"updated_at": "2024-11-22 08:40:42 +0530",
// MR访问地址
"url": "http://gitlab.tiger.oa.com/root/luhao/-/merge_requests/31",
// 源仓库信息
"source": {
"id": 2,
"name": "luhao",
"description": "仓库描述",
"web_url": "http://gitlab.tiger.oa.com/root/luhao",
"path_with_namespace": "root/luhao",
"homepage": "http://gitlab.tiger.oa.com/root/luhao"
},
// 目标仓库信息
"target": {
"id": 2,
"name": "luhao",
"description": "仓库描述",
"web_url": "http://gitlab.tiger.oa.com/root/luhao",
"path_with_namespace": "root/luhao",
"homepage": "http://gitlab.tiger.oa.com/root/luhao",
},
"state": "opened",
// merge_request 动作, 目前只接收:
// open 打开
// merge 已合并
// close 已关闭
"action": "open"
},
// 当前操作仓库信息
"repository": {
// 仓库名
"name": "luhao",
// 仓库描述
"description": "仓库描述",
// 仓库访问地址
"homepage": "http://gitlab.tiger.oa.com/root/luhao",
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# hooks
# hooks.get
获取已经注册的hook回调url列表
# 请求参数
{
"query": {
"project_id": 1, //scm project id
}
}
1
2
3
4
5
2
3
4
5
# 期望返回
[
// 多个回调配置
{
"id":1,
"url":"http://hook.tapd.cn/10000000/xxxxx",
// 支持的回调事件
"push_events":true,
"tag_push_events":false,
"merge_requests_events": true,
"repository_update_events": true,
"enable_ssl_verification":true
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# hooks.post
新增hook配置
# 请求参数
{
"query": {
"project_id": 1, //scm project id
},
"post": {
"id": 1, //scm project id
"url": "http://hook.tapd.cn/10000000/xxxxx", // 回调地址
// 支持的回调事件,插件不需要严格按照这里传递的时间类型注册,按需注册即可
// push事件
"push_events": 1,
// MR事件
"merge_requests_events": 1,
// Review事件
"review_events": 1,
// 是否支持token(可选)
"token": "xxxxxx",
"enable_ssl_verification": true,
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 期望返回
{
"id":1,
"url":"http://hook.tapd.cn/10000000/xxxxx",
// 支持的回调事件
"push_events":true,
"tag_push_events":false,
"merge_requests_events": true,
"repository_update_events": true,
"enable_ssl_verification":true
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# hook.put
更新一个已存在hook的配置
# 请求参数
{
"query": {
"project_id": 1, //scm project id
"hook_id": 1, // hook id
},
"post": {
// 支持的回调事件,插件不需要严格按照这里传递的时间类型注册,按需注册即可
// push事件
"push_events": 1,
// MR事件
"merge_requests_events": 1,
// Review事件
"review_events": 1
// 是否支持token(可选)
"token": "xxxxxx",
"enable_ssl_verification": true,
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 期望返回
无
# hook.delete
删除一个已有hook配置(当有重复配置的hook url时触发)
# 请求参数
{
"query": {
"project_id": 1, //scm project id
"hook_id": 1, // hook id
},
"post": {
"project_id": 1, //scm project id
"hook_id": 1, // hook id
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 期望返回
无
# projects
# projects.get
查询项目列表
# 请求参数
{
"query": {
"search": "project", //search keywork
"page": 1,
"per_page": 10
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 期望返回
[
{
"id": 1, // scm project id
"name_with_namespace": "Test / projectX", // scm project name with namespace
"path_with_namespace": "test / projectx", // scm project path with namespace
"ssh_url_to_repo": "git@git.tencent.com:test/projectx.git", // scm project ssh url
"http_url_to_repo": "https://git.tencent.com/test/projectx.git", // scm project http url
"web_url": "http://git.tencent.com/test/projectx", // scm project web url
},
{
"id": 2, // scm project id
"name_with_namespace": "Test / projectY", // scm project name with namespace
"path_with_namespace": "test / projecty", // scm project path with namespace
"ssh_url_to_repo": "git@git.tencent.com:test/projecty.git", // scm project ssh url
"http_url_to_repo": "https://git.tencent.com/test/projecty.git", // scm project http url
"web_url": "http://git.tencent.com/test/projecty", // scm project web url
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# project.get
根据project_id获取项目详情
# 请求参数
{
"query": {
"project_id": 1, //scm project id
}
}
1
2
3
4
5
2
3
4
5
# 期望返回
{
"id": 1, // scm project id
"description": "this is my first project", // scm project description
"name": "projectX", // scm project name
"name_with_namespace": "Test / projectX", // scm project name with namespace
"path": "projectx", // scm project path
"path_with_namespace": "test / projectx", // scm project path with namespace
"default_branch": "master", // scm default branch name
"ssh_url_to_repo": "git@git.tencent.com:test/projectx.git", // scm project ssh url
"http_url_to_repo": "https://git.tencent.com/test/projectx.git", // scm project http url
"web_url": "http://git.tencent.com/test/projectx", // scm project web url
"created_at": "2023-09-30T13:46:02Z",
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# commits
# query_commits.get
查询commit列表
# 请求参数
{
"query": {
"project_id": 1, // scm project id
"search": "commit", // search keyword
"author": "finalsun", // commit author
"branch_name": "master", // commit branch
"begin_date": "2024-02-12T00:00:00+0800",
"end_date": "2024-03-11T23:59:59+0800",
"page": 1,
"per_page": 10
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 期望返回
[
{
"id": "ed899a2f4b50b4370feeea94676502b42383c746", // scm commit id
"commit": "ed899a2f4b50b4370feeea94676502b42383c746", // scm commit id
"message": "commit test", // scm commit message
"author_name": "finslun",
"authored_date": "2024-02-20T11:50:22.001+00:00",
}
]
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# commit.get
根据commit_id获取commit详情
# 请求参数
{
"query": {
"project_id": 1, // scm project id
"commit_id": "ed899a2f4b50b4370feeea94676502b42383c746" // scm commit id
}
}
1
2
3
4
5
6
2
3
4
5
6
# 期望返回
{
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"message": "commit test",
"parent_ids": [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
],
"author_name": "finalsun",
"author_email": "finalsun@tencent.com",
"committer_name": "finalsun",
"committer_email": "finalsun@tencent.com",
"title": "commit test",
"short_id": "ed899a2f4b5",
"unique_commit_id": "ed899a2f4b50b4370feeea94676502b42383c746",
"path": "https://git.code.tencent.com/test/projectx/commit/ed899a2f4b50b4370feeea94676502b42383c746",
"authored_date": "2024-09-20T09:06:12.420+08:00",
"committed_date": "2024-09-20T09:06:12.420+08:00",
"created_at": "2024-09-20T09:06:12.420+08:00",
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# commit_diff.get
获取单个commit的变更列表
# 请求参数
{
"query": {
"project_id": 1, // scm project id
"commit_id": "ed899a2f4b50b4370feeea94676502b42383c746" // scm commit id
}
}
1
2
3
4
5
6
2
3
4
5
6
# 期望返回
[
// 变更文件列表
{
"new_path": "doc/update/5.4-to-6.0.md",
"old_path": "doc/update/5.4-to-6.0.md",
"new_file": false, // 是否为新增文件
"renamed_file": false, // 是否为重命名文件
"deleted_file": false // 是否为删除文件
},
{
"new_path": "doc/update/t.md",
"old_path": "",
"new_file": true, // 是否为新增文件
"renamed_file": false, // 是否为重命名文件
"deleted_file": false // 是否为删除文件
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# branches
# branches.get
查询分支列表
# 请求参数
{
"query": {
"project_id": 1, // scm project id
"search": "m", // search keyword
"page": 1,
"per_page": 20
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 期望返回
[
{
"name": "master",
// 分支最新commit信息
"commit": {
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"message": "commit test",
"author_name": "finalsun",
"authored_date": "2024-09-20T09:06:12.420+03:00"
}
},
{
"name": "m3",
// 分支最新commit信息
"commit": {
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"message": "commit test",
"author_name": "finalsun",
"authored_date": "2024-09-20T09:06:12.420+03:00"
}
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# branches.post
创建新分支
# 请求参数
{
"query": {
"project_id": 1, // scm project id
},
"json": {
"project_id": 1, // scm project id
"branch_name": "new_branch", // new branch name
"ref": "master", // create branch from
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 期望返回
{
"message": "create branch success",
}
1
2
3
2
3
# merge_requests
# merge_requests.get
获取单个MR详情
# 请求参数
{
"query": {
"project_id": 1, // scm project id
// scm merge request id
"iids": [
1123,
1124
]
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 期望返回
[
{
"title": "mr1", // merge request title
"state": "opened", // MR state, opened/reopened/closed/merged
"mr_url": "git.code.tencent.com/test/merge_requests/1123", // mr url
"created_at": "2024-09-20T09:06:12.420+08:00",
"updated_at": "2024-09-20T09:06:12.420+08:00",
"iid": 1123,
"author": {
"username": "finalsun"
},
"source_commit": "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba",
"target_commit": "ed899a2f4b50b4370feeea94676502b42383c746",
"necessary_reviewers": [
{
"username": "willisgao"
},
{
"username": "boobohuang"
},
],
"suggestion_reviewers": [],
"merged_at": "", // merged time, only valid when state is merged
"target_project_id": 1,
"source_project_id": 1,
"target_branch": "m3",
"source_branch": "master",
},
{
"title": "mr2", // merge request title
"state": "merged", // MR state, opened/reopened/closed/merged
"mr_url": "git.code.tencent.com/test/merge_requests/1124", // mr url
"created_at": "2024-09-20T09:06:12.420+08:00",
"updated_at": "2024-09-20T09:06:12.420+08:00",
"iid": 1124,
"author": {
"username": "finalsun"
},
"source_commit": "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba",
"target_commit": "ed899a2f4b50b4370feeea94676502b42383c746",
"necessary_reviewers": [
{
"username": "willisgao"
},
{
"username": "boobohuang"
},
],
"suggestion_reviewers": [],
"merged_at": "2024-09-20T09:10:07.398+08:00", // merged time, only valid when state is merged
"target_project_id": 1,
"source_project_id": 1,
"target_branch": "m3",
"source_branch": "master",
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# merge_requests.get_mr_url
根据分支名获取创建MR的url
# 请求参数
{
"query": {
"branch_name": "m3",
}
}
1
2
3
4
5
2
3
4
5
# 期望返回
https://git.code.tencent.com/test/merge_requests/master...m3
1
# 使用示例
在任意工作项中使用 SCM 插件应用进行关联

# 插件工程目录示例
├── handler
│ └── config.js
├── index.js
├── Makefile
├── modules
│ ├── autotasks
│ │ └── condition.js
│ └── scm
│ ├── commit.js
│ ├── hook.js
│ ├── projects.js
│ ├── sdk.js
│ └── storage
│ ├── config.js
│ └── storage.js
├── package.json
├── plugin.yaml
├── resources
│ └── pages
│ ├── app_for_global_config
│ │ ├── app_for_global_config.vue
│ │ └── index.css
│ ├── app_for_global_config.js
│ └── index.html
├── utils.js
└── webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26