# 前言

这里介绍如何使用 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

# 接口实现

# SCM应用hook格式

TAPD接收到第三方SCM的回调数据之后, 会检查插件是否配置了_hooks_handler处理器,如果未配置,则根据默认解析逻辑处理回调数据,如果配置了,则调用插件的_hooks_handler处理回调数据。插件中需要根据事件类型将回调数据转换为TAPD需要的格式。

# plugin.yaml定义:
scm:
  adapter:
    _hooks_handler:
      adapter: hook.handler
1
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
# 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
# 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

# hooks

# hooks.get

获取已经注册的hook回调url列表

# 请求参数
{
    "query": {
        "project_id": 1, //scm project id
    }
}
1
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
# 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
# 期望返回
{
    "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
# 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
# 期望返回

# 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
# 期望返回

# projects

# projects.get

查询项目列表

# 请求参数
{
    "query": {
        "search": "project", //search keywork
        "page": 1, 
        "per_page": 10
    }
}
1
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
# project.get

根据project_id获取项目详情

# 请求参数
{
    "query": {
        "project_id": 1, //scm project id
    }
}
1
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

# 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
# 期望返回
[
    {
        "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
# commit.get

根据commit_id获取commit详情

# 请求参数
{
    "query": {
        "project_id": 1, // scm project id
        "commit_id": "ed899a2f4b50b4370feeea94676502b42383c746" // scm commit id
    }
}
1
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
# commit_diff.get

获取单个commit的变更列表

# 请求参数
{
    "query": {
        "project_id": 1, // scm project id
        "commit_id": "ed899a2f4b50b4370feeea94676502b42383c746" // scm commit id
    }
}
1
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

# 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
# 期望返回
[
    {
        "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
# 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
# 期望返回
{
    "message": "create branch success",
}
1
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
# 期望返回
[
    {
        "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
# merge_requests.get_mr_url

根据分支名获取创建MR的url

# 请求参数
{
    "query": {
        "branch_name": "m3",
    }
}
1
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
上次更新: 2025-02-10 17:17:33