开源端到端流水线实践-需求与代码管理

开发 前端
业务的简称为demo,微服务架构。N多个微服务。服务命名:业务简称-应用名称-类型(demo-hello-service)。特性分支开发,版本分支发布。每个需求(任务/故事)对应一个特性分支。每个发布(release)对应一个版本分支。

 [[348244]]

业务的简称为demo,微服务架构。N多个微服务。服务命名:业务简称-应用名称-类型(demo-hello-service)。特性分支开发,版本分支发布。每个需求(任务/故事)对应一个特性分支。每个发布(release)对应一个版本分支。

1.需求与代码管理
Jira作为需求和缺陷管理,采用Scrum开发方法,jira中的项目名称与业务简称一致(demo)。Gitlab作为版本控制系统,每个Group对应一个业务,每个微服务对应一个代码库。

需求与代码关联:在jira中创建一个任务/故事,关联模块后自动在该模块创建一个以ISSUE(任务/故事)ID的特性分支。此时的模块等同于每个微服务的项目(代码库)名称。以下面图中为例:我们在demo项目中创建了一个模块demo-hello-service,其实对应的就是Gitlab代码库中demo组的demo-hello-service服务。

特性分支:创建好每个模块后,就可以实现需求与代码关联。例如:我们在Jira项目demo中创建一个问题,类型为故事(不受限制可为其他),重点是需要将改故事关联到模块(只有关联到模块,我们才能通过接口得知哪个问题关联的哪个代码库)。

版本分支:当特性分支开发完成以及测试验证完成后,基于主干分支创建一个版本分支,然后将所有的特性分支合并到版本分支。此时可以通过Jira中创建一个发布版本,然后问题关联发布版本(此动作表示该特性分支已经通过验证,可以合并)。自动完成版本分支的创建和特性分支到版本分支的合并请求。

2. 配置过程
需求与代码库关联,主要用到的工具链为: Jira + GitLab + Jenkins。Jira负责创建需求,配置webhook。Jenkins负责接收Jira webhook请求,然后通过接口实现GitLab项目分支创建。

特性分支自动化:当我们在jira上面创建了问题,此时会通过Jira的webhook触发对应的Jenkins作业,该Jenkins作业通过解析Jira webhook传递的数据,找到问题名称和模块名称。调用GitlabAPI 项目查询接口,根据模块名称找到代码库。调用GitLabAPI 分支创建接口,根据问题名称基于主干分支创建一个特性分支。任务结束。

版本分支自动化:Jira创建发布版本,Issue关联版本。自动在gitlab代码库基于master创建版本分支,并开启特性分支到版本分支的合并请求。

2.1 准备工作
在Jenkins, 创建一个Pipeline 作业并配置GenericTrigger 触发器,接收JiraWebhook数据。projectKey 参数表示Jira项目名称,webHookData 参数为Jira webhook的所有数据。token 是触发器的触发token,这里默认采用的作业名称(作业名称要唯一)。

  1. triggers { 
  2.         GenericTrigger( causeString: 'Trigger By Jira Server -->>>>> Generic Cause',  
  3.                         genericRequestVariables: [[key'projectKey', regexpFilter: '']],  
  4.                         genericVariables: [[defaultValue: ''key'webHookData', regexpFilter: '', value: '$']],  
  5.                         printContributedVariables: true,  
  6.                         printPostContent: true,  
  7.                         regexpFilterExpression: '',  
  8.                         regexpFilterText: '',  
  9.                         silentResponse: true,  
  10.                         token: "${JOB_NAME}" 
  11.         ) 

在Jira项目中配置Webhook,勾选触发事件填写触发URL。http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=demo-jira-service&projectKey=${project.key} (这个地址是jenkins Generictrigger生成的,这里不做过多的介绍)

Jira webhook数据参考, 这些参数可以在Jenkinsfile中通过readJSON格式化,然后获取值。

  1. response = readJSON text: """${webHookData}""" 
  2. println(response) 
  3.  
  4. //获取webhook的事件类型 
  5. env.eventType = response["webhookEvent"
  1.     "timestamp":1603087582648, 
  2.     "webhookEvent":"jira:issue_created"
  3.     "issue_event_type_name":"issue_created"
  4.     "user":Object{...}, 
  5.     "issue":{ 
  6.         "id":"10500"
  7.         "self":"http://192.168.1.200:8050/rest/api/2/issue/10500"
  8.         "key":"DEMO-2"
  9.         "fields":{ 
  10.             "issuetype":{ 
  11.                 "self":"http://192.168.1.200:8050/rest/api/2/issuetype/10001"
  12.                 "id":"10001"
  13.                 "description":""
  14.                 "iconUrl":"http://192.168.1.200:8050/images/icons/issuetypes/story.svg"
  15.                 "name":"故事"
  16.                 "subtask":false 
  17.             }, 
  18.             "components":[ 
  19.                 { 
  20.                     "self":"http://192.168.1.200:8050/rest/api/2/component/10200"
  21.                     "id":"10200"
  22.                     "name":"demo-hello-service"
  23.                     "description":"demo-hello-service应用" 
  24.                 } 
  25.             ], 
  26.             "timespent":null
  27.             "timeoriginalestimate":null
  28.             "description":null
  29.             ... 
  30.             ... 
  31.             ... 

2.2 封装GitLab接口
Gitlab接口文档:https://docs.gitlab.com/ce/api/README.html

共享库:src/org/devops/gitlab.groovy

  1. package org.devops 
  2.  
  3. //封装HTTP请求 
  4. def HttpReq(reqType,reqUrl,reqBody){ 
  5.     def gitServer = "http://gitlab.idevops.site/api/v4" 
  6.     withCredentials([string(credentialsId: 'gitlab-token', variable: 'gitlabToken')]) { 
  7.       result = httpRequest customHeaders: [[maskValue: truename'PRIVATE-TOKEN', value: "${gitlabToken}"]],  
  8.                 httpMode: reqType,  
  9.                 contentType: "APPLICATION_JSON"
  10.                 consoleLogResponseBody: true
  11.                 ignoreSslErrors: true,  
  12.                 requestBody: reqBody, 
  13.                 url: "${gitServer}/${reqUrl}" 
  14.                 //quiet: true 
  15.     } 
  16.     return result 
  17.  
  18.  
  19. //更新文件内容 
  20. def UpdateRepoFile(projectId,filePath,fileContent){ 
  21.     apiUrl = "projects/${projectId}/repository/files/${filePath}" 
  22.     reqBody = """{"branch": "master","encoding":"base64", "content": "${fileContent}", "commit_message": "update a new file"}""" 
  23.     response = HttpReq('PUT',apiUrl,reqBody) 
  24.     println(response) 
  25.  
  26.  
  27. //获取文件内容 
  28. def GetRepoFile(projectId,filePath){ 
  29.     apiUrl = "projects/${projectId}/repository/files/${filePath}/raw?ref=master" 
  30.     response = HttpReq('GET',apiUrl,''
  31.     return response.content 
  32.  
  33. //创建仓库文件 
  34. def CreateRepoFile(projectId,filePath,fileContent){ 
  35.     apiUrl = "projects/${projectId}/repository/files/${filePath}" 
  36.     reqBody = """{"branch": "master","encoding":"base64", "content": "${fileContent}", "commit_message": "create a new file"}""" 
  37.     response = HttpReq('POST',apiUrl,reqBody) 
  38.     println(response) 
  39.  
  40.  
  41. //更改提交状态 
  42. def ChangeCommitStatus(projectId,commitSha,status){ 
  43.     commitApi = "projects/${projectId}/statuses/${commitSha}?state=${status}" 
  44.     response = HttpReq('POST',commitApi,''
  45.     println(response) 
  46.     return response 
  47.  
  48. //获取项目ID 
  49. def GetProjectID(repoName='',projectName){ 
  50.     projectApi = "projects?search=${projectName}" 
  51.     response = HttpReq('GET',projectApi,''
  52.     def result = readJSON text: """${response.content}""" 
  53.      
  54.     for (repo in result){ 
  55.        // println(repo['path_with_namespace']) 
  56.         if (repo['path'] == "${projectName}"){ 
  57.              
  58.             repoId = repo['id'
  59.             println(repoId) 
  60.         } 
  61.     } 
  62.     return repoId 
  63.  
  64. //删除分支 
  65. def DeleteBranch(projectId,branchName){ 
  66.     apiUrl = "/projects/${projectId}/repository/branches/${branchName}" 
  67.     response = HttpReq("DELETE",apiUrl,'').content 
  68.     println(response) 
  69.  
  70. //创建分支 
  71. def CreateBranch(projectId,refBranch,newBranch){ 
  72.     try { 
  73.         branchApi = "projects/${projectId}/repository/branches?branch=${newBranch}&ref=${refBranch}" 
  74.         response = HttpReq("POST",branchApi,'').content 
  75.         branchInfo = readJSON text: """${response}""" 
  76.     } catch(e){ 
  77.         println(e) 
  78.     }  //println(branchInfo) 
  79.  
  80. //创建合并请求 
  81. def CreateMr(projectId,sourceBranch,targetBranch,title,assigneeUser=""){ 
  82.     try { 
  83.         def mrUrl = "projects/${projectId}/merge_requests" 
  84.         def reqBody = """{"source_branch":"${sourceBranch}", "target_branch": "${targetBranch}","title":"${title}","assignee_id":"${assigneeUser}"}""" 
  85.         response = HttpReq("POST",mrUrl,reqBody).content 
  86.         return response 
  87.     } catch(e){ 
  88.         println(e) 
  89.     } 
  90.  
  91. //搜索分支 
  92. def SearchProjectBranches(projectId,searchKey){ 
  93.     def branchUrl =  "projects/${projectId}/repository/branches?search=${searchKey}" 
  94.     response = HttpReq("GET",branchUrl,'').content 
  95.     def branchInfo = readJSON text: """${response}""" 
  96.      
  97.     def branches = [:] 
  98.     branches[projectId] = [] 
  99.     if(branchInfo.size() ==0){ 
  100.         return branches 
  101.     } else { 
  102.         for (branch in branchInfo){ 
  103.             //println(branch) 
  104.             branches[projectId] += ["branchName":branch["name"], 
  105.                                     "commitMes":branch["commit"]["message"], 
  106.                                     "commitId":branch["commit"]["id"], 
  107.                                     "merged": branch["merged"], 
  108.                                     "createTime": branch["commit"]["created_at"]] 
  109.         } 
  110.         return branches 
  111.     } 
  112.  
  113. //允许合并 
  114. def AcceptMr(projectId,mergeId){ 
  115.     def apiUrl = "projects/${projectId}/merge_requests/${mergeId}/merge" 
  116.     HttpReq('PUT',apiUrl,''

2.3 共享库配置

演示效果:上传了两个小视频,可以扫描进入视频号查看。

 

 

责任编辑:姜华 来源: DevOps云学堂
相关推荐

2021-04-29 08:55:54

GitLabDevOps项目

2017-03-02 14:12:13

流水线代码Clojure

2022-07-18 06:05:28

Gitlab流水线

2024-01-07 12:47:35

Golang流水线设计模式

2023-12-11 18:35:37

测试流水线自动化

2021-04-13 06:15:37

开源部署流水线Jenkins

2021-12-24 08:02:48

GitLabCI模板库流水线优化

2019-11-07 09:00:39

Jenkins流水线开源

2017-02-28 16:00:45

DevOpsMarkdownreST

2023-05-10 15:08:00

Pipeline设计模式

2023-05-26 08:31:09

2017-02-28 15:40:30

Docker流水线Azure

2013-06-06 09:31:52

2021-11-08 07:41:16

Go流水线编程

2022-01-26 08:12:42

Jenkins开源流水线

2021-06-26 14:22:34

Tekton流水线Kubernetes

2023-08-18 10:24:52

GitLabCI 流水线

2019-11-07 10:02:33

开源开源工具DevOps

2021-06-28 06:32:46

Tekton Kubernetes Clone

2021-10-12 08:47:01

Nexus存储库管理器DevOps
点赞
收藏

51CTO技术栈公众号