需求管理平台集成

本章我们主要讲述Jenkins与需求管理平台Jira集成。这篇文章是我根据我们当前团队的情况进行定制的,可能有些内容与大家不太一样。重点是告诉大家如何实现集成?关于细节问题可自由定制。 在这里要告诉大家的是思路。

目录


在这里先说一下,当前的现状。我们使用Jira的Git插件能够实现在Jira上面新建Gitlab分支(实现需求与代码基线关联),这个操作需要手动完成。使用Jira中的Git插件实现集成,能够实现在Jira系统对应的任务下面创建分支,或开发人员在创建分支的时候分支名称包含Jira ID则可以自动关联。 通过上面的描述你应该发现了存在的一些问题,例如:插件不稳定,资源消耗大。手动创建分支,增加了人员成本。

来我们先看下当前的分支策略(每个团队可能会有自己的一套分支策略)。 images

开发人员在特性分支进行开发,基于master分支创建一个版本分支(用于发布)。特性分支在特性环境验证通过后,将特性分支合并到版本分支,此时通过版本分支进行UAT测试环境、STAG预生产环境验证测试,测试通过通过版本分支发布PROD生产环境。生产环境发布完成后,将版本分支合并到master分支。

为解决上述问题,制作了改进方案。 我们想实现在Jira中创建故事或者任务(Jira中的issue)时自动在对应的代码库创建分支,故事或者任务在关联到发布(Jira中的release)之后自动在代码库中创建一个release分支和特性分支到release分支的合并请求。

工具链调研分析

其实我们需要配置的是通过Jira的Webhook 触发Jenkins来操作Gitlab系统。主要有三个点:

  • Jenkins项目配置触发器接收触发请求
  • Jira系统配置webHook触发Jenkins
  • Jenkins封装GitlabAPI完成相关操作

Jira调研与分析-webhook

关于Jira的webhook挺强大的几乎所有的事件都能支持,我们进入jira系统的管理页面,选择webhook。

填写配置触发器URL(Jenkins项目触发器地址)、通过JQL(Jira的一种查询语言类似于SQL)配置触发的项目、选择触发事件。

images images

Jira调研与分析-webhookevent

在上面我们知道有webhook的概念,我们来分析要完成我们的需求所需要的事件类型。其实这些数据是在webhook中传递。Jira的事件类型如上图所示有很多,在这里我们用到了 问题创建jira:issue_created、问题更新jira:issue_update

  • 创建任务或者故事属于jira:issue_created事件。
  • 任务或者故事关联上版本属于jira:issue_updated事件。

来一起看下Jira的webhook传递的JSON数据,如下图所示:

images

到此可以总结为Jira的功能已经完全满足了我们的需求。

Gitlab调研与分析

关于Gitlab要做的事情有2点,分别为:

  • 创建分支(branch)
  • 创建合并请求(merge request)

我们可以参考Gitlab的接口文档 https://docs.gitlab.com/ce/api/api_resources.html, 找出我们所需要的API。如下所示:

  1. # 获取项目ID接口
  2. api/v4/projects?search=projectName
  3. # 创建分支接口
  4. api/v4/projects/${projectId}/repository/branches?branch=${newBranch}&ref=${refBranch}
  5. # 创建合并请求接口
  6. api/v4/projects/${projectId}/merge_requests

{{% notice info %}} 我们找到了以上三个接口,是不是疑问获取项目ID接口是用来做什么的呢? 其实这个就是用来通过项目名称获取项目的ID的。要想创建分支或者合并请求都是需要项目的ID的。项目名称就是Jira中的模块名称,也就是在Jira创建issue的时候必须要给此issue关联一个模块。后续我们根据此模块名称调用gitlab接口查询获取ID,以便进行后续的操作。 {{% /notice %}}

Jenkins调研与分析- Generic Webhook Trigger

我们使用Generic Webhook Trigger来完成此次任务, 对于Jenkins来说,需要做的有3点:

  • 配置触发器接收来自Jira的请求
  • 分析Jira请求中传递的数据
  • 封装Gitlab API完成相关操作

分析数据 获取hook中参数-获取问题名称-获取模块名称等数据 images

配置触发器 生成项目唯一Token-使用项目名称标识 images

配置触发过滤器 过滤触发请求-限制只有故事任务等触发(在Jira中issue类型有很多种,这里可以灵活定义) images

实践

上面的技术点你是否清楚了呢? 接下来我们要上战场实践了。

创建issue自动创建gitlab分支

我想大概过程是这样的:

  • Jira: 我来创建与gitlab项目名称一致的模块。然后我来创建issue关联上模块。
  • 此时通过webhook触发了Jenkins的项目。
  • Jenkins: 我来接收来自Jira的请求,我在请求中获取到issueName,components等参数。
  • Jenkins: 通过gitlab查询项目接口根据components来获取gitlab项目的ID。
  • Jenkins: 然后根据以获取的项目ID和issueName参数,通过gitlab创建分支接口创建分支。

技术实现细节是这样的

要想实现在gitlab中创建分支,接口我们已经找到了,但是如何获取每个issue所关联的项目名称呢?我们自己维护一个关系表?当一个issue需要关联多个项目呢? 此时我们需要用到jira中的模块。使用jira中的模块来作为gitlab中的项目(命名一致),我们在创建issue的同时需要关联模块(必须关联,否则创建不了哦)。所以第一件事情是在Jira中创建模块,模块命名同Gitlab项目名称

此时我创建了一个模块demo-maven-service(我的gitlab项目名称也是这个) images

然后我(Jira)来创建issues,我创建一个任务,然后关联demo-maven-service模块 。此时证明:这个任务关联的是demo-maven-service这个代码库。也就是我要在demo-maven-service项目下创建一个issueName同名的分支(特性分支)。

images

触发Jenkins项目: Jenkins 根据模块(components)在对应的仓库中创建分支(IssueName)。JIRA更新issues,Jenkins 根据模块(components)在对应的仓库中创建分支(issuesName)。

images images

我的Jenkinsfile内容如下: 在这里我引入了我们自己封装好的gitlab.groovy,首先第一个步骤是FileterData分析数据,写了一个 switch语句来根据不同的event来获取不同的数据。jira:issue_created问题创建事件,我们需要获取的参数有问题名称、触发人、关联模块、关联版本。 然后第二个步骤是CreateBranch,我们通过gitlab的接口获取项目的ID,因为一个issue可能关联多个模块,所以这里是个list列表,后面遍历这个列表然后创建在对应的项目中创建分支。

  1. #!groovy
  2. @Library('jenkinslibrary') _
  3. def gitlab = new org.devops.gitlab()
  4. pipeline {
  5. agent { node { label "master"}}
  6. stages{
  7. stage("FileterData"){
  8. steps{
  9. script{
  10. response = readJSON text: """${webHookData}"""
  11. println(response)
  12. env.eventType = response["webhookEvent"]
  13. switch(eventType) {
  14. case "jira:issue_created":
  15. env.issueName = response['issue']['key']
  16. env.userName = response['user']['name']
  17. env.moduleNames = response['issue']['fields']['components']
  18. env.fixVersion = response['issue']['fields']['fixVersions']
  19. currentBuild.description = " Trigger by ${userName} ${eventType} ${issueName} "
  20. break
  21. default:
  22. println("hello")
  23. }
  24. }
  25. }
  26. }
  27. stage("CreateBranch"){
  28. when {
  29. anyOf {
  30. environment name: 'eventType', value: 'jira:issue_created' //issue 创建 /更新
  31. environment name: 'eventType', value: 'jira:issue_updated'
  32. }
  33. }
  34. steps{
  35. script{
  36. def projectIds = []
  37. println(issueName)
  38. //获取项目Id
  39. def projects = readJSON text: """${moduleNames}"""
  40. for ( project in projects){
  41. println(project["name"])
  42. projectName = project["name"]
  43. currentBuild.description += "\n project: ${projectName}"
  44. repoName = projectName.split("-")[0]
  45. try {
  46. projectId = gitlab.GetProjectID(repoName, projectName)
  47. println(projectId)
  48. projectIds.add(projectId)
  49. } catch(e){
  50. println(e)
  51. println("未获取到项目ID,请检查模块名称!")
  52. }
  53. }
  54. println(projectIds)
  55. for (id in projectIds){
  56. println("新建特性分支--> ${id} --> ${issueName}")
  57. currentBuild.description += "\n 新建特性分支--> ${id} --> ${issueName}"
  58. gitlab.CreateBranch(id,"master","${issueName}")
  59. }
  60. }
  61. }
  62. }
  63. }
  64. }

最终效果,登录gitlab进入项目中你应该能够看到跟当前issue同名的一个特性分支。 images

{{% notice info %}} 为了避免重复创建分支导致流水线失败,我们需要在写gitlab方法时加上一个try catch捕获异常。 {{% /notice %}}

自动创建合并请求

JIRA更新issues到版本中。 Jenkins根据issues中的模块信息,在对应的仓库中创建release分支(fixVersion),并创建特性分支(issueName)到release分支的MR。 jira的issue更新事件有两个情况第一种是增加关联的模块,还有一种是将issue关联了版本。

我们先来创建一个版本 images 然后将issue关联一个版本

images

完善Jenkinsfile

  1. @Library('jenkinslibrary') _
  2. def gitlab = new org.devops.gitlab()
  3. pipeline {
  4. agent { node { label "master"}}
  5. stages{
  6. stage("FileterData"){
  7. steps{
  8. script{
  9. response = readJSON text: """${webHookData}"""
  10. println(response)
  11. env.eventType = response["webhookEvent"]
  12. switch(eventType) {
  13. case "jira:issue_created":
  14. env.issueName = response['issue']['key']
  15. env.userName = response['user']['name']
  16. env.moduleNames = response['issue']['fields']['components']
  17. env.fixVersion = response['issue']['fields']['fixVersions']
  18. currentBuild.description = " Trigger by ${userName} ${eventType} ${issueName} "
  19. break
  20. case "jira:issue_updated":
  21. env.issueName = response['issue']['key']
  22. env.userName = response['user']['name']
  23. env.moduleNames = response['issue']['fields']['components']
  24. env.fixVersion = response['issue']['fields']['fixVersions']
  25. currentBuild.description = " Trigger by ${userName} ${eventType} ${issueName} "
  26. break
  27. default:
  28. println("hello")
  29. }
  30. }
  31. }
  32. }
  33. stage("CreateBranchOrMR"){
  34. when {
  35. anyOf {
  36. environment name: 'eventType', value: 'jira:issue_created' //issue 创建 /更新
  37. environment name: 'eventType', value: 'jira:issue_updated'
  38. }
  39. }
  40. steps{
  41. script{
  42. def projectIds = []
  43. println(issueName)
  44. fixVersion = readJSON text: """${fixVersion}"""
  45. println(fixVersion.size())
  46. //获取项目Id
  47. def projects = readJSON text: """${moduleNames}"""
  48. for ( project in projects){
  49. println(project["name"])
  50. projectName = project["name"]
  51. currentBuild.description += "\n project: ${projectName}"
  52. repoName = projectName.split("-")[0]
  53. try {
  54. projectId = gitlab.GetProjectID(repoName, projectName)
  55. println(projectId)
  56. projectIds.add(projectId)
  57. } catch(e){
  58. println(e)
  59. println("未获取到项目ID,请检查模块名称!")
  60. }
  61. }
  62. println(projectIds)
  63. if (fixVersion.size() == 0) {
  64. for (id in projectIds){
  65. println("新建特性分支--> ${id} --> ${issueName}")
  66. currentBuild.description += "\n 新建特性分支--> ${id} --> ${issueName}"
  67. gitlab.CreateBranch(id,"master","${issueName}")
  68. }
  69. } else {
  70. fixVersion = fixVersion[0]['name']
  71. println("Issue关联release操作,Jenkins创建合并请求")
  72. currentBuild.description += "\n Issue关联release操作,Jenkins创建合并请求 \n ${issueName} --> RELEASE-${fixVersion}"
  73. for (id in projectIds){
  74. println("创建RELEASE-->${id} -->${fixVersion}分支")
  75. gitlab.CreateBranch(id,"master","RELEASE-${fixVersion}")
  76. println("创建合并请求 ${issueName} ---> RELEASE-${fixVersion}")
  77. gitlab.CreateMr(id,"${issueName}","RELEASE-${fixVersion}","${issueName}--->RELEASE-${fixVersion}")
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }
  84. }

Gitlab效果:查看合并请求 images images

总结

通过以上两个实践,相信你已经知道了如何实现系统间的集成,没错主要是通过webhook和api。 接下来如果有需求可以独立去灵活定义了哦。