開源端到端流水線實(shí)踐-需求與代碼管理

業(yè)務(wù)的簡稱為demo,微服務(wù)架構(gòu)。N多個微服務(wù)。服務(wù)命名:業(yè)務(wù)簡稱-應(yīng)用名稱-類型(demo-hello-service)。特性分支開發(fā),版本分支發(fā)布。每個需求(任務(wù)/故事)對應(yīng)一個特性分支。每個發(fā)布(release)對應(yīng)一個版本分支。
1.需求與代碼管理
Jira作為需求和缺陷管理,采用Scrum開發(fā)方法,jira中的項(xiàng)目名稱與業(yè)務(wù)簡稱一致(demo)。Gitlab作為版本控制系統(tǒng),每個Group對應(yīng)一個業(yè)務(wù),每個微服務(wù)對應(yīng)一個代碼庫。

需求與代碼關(guān)聯(lián):在jira中創(chuàng)建一個任務(wù)/故事,關(guān)聯(lián)模塊后自動在該模塊創(chuàng)建一個以ISSUE(任務(wù)/故事)ID的特性分支。此時的模塊等同于每個微服務(wù)的項(xiàng)目(代碼庫)名稱。以下面圖中為例:我們在demo項(xiàng)目中創(chuàng)建了一個模塊demo-hello-service,其實(shí)對應(yīng)的就是Gitlab代碼庫中demo組的demo-hello-service服務(wù)。

特性分支:創(chuàng)建好每個模塊后,就可以實(shí)現(xiàn)需求與代碼關(guān)聯(lián)。例如:我們在Jira項(xiàng)目demo中創(chuàng)建一個問題,類型為故事(不受限制可為其他),重點(diǎn)是需要將改故事關(guān)聯(lián)到模塊(只有關(guān)聯(lián)到模塊,我們才能通過接口得知哪個問題關(guān)聯(lián)的哪個代碼庫)。

版本分支:當(dāng)特性分支開發(fā)完成以及測試驗(yàn)證完成后,基于主干分支創(chuàng)建一個版本分支,然后將所有的特性分支合并到版本分支。此時可以通過Jira中創(chuàng)建一個發(fā)布版本,然后問題關(guān)聯(lián)發(fā)布版本(此動作表示該特性分支已經(jīng)通過驗(yàn)證,可以合并)。自動完成版本分支的創(chuàng)建和特性分支到版本分支的合并請求。

2. 配置過程
需求與代碼庫關(guān)聯(lián),主要用到的工具鏈為: Jira + GitLab + Jenkins。Jira負(fù)責(zé)創(chuàng)建需求,配置webhook。Jenkins負(fù)責(zé)接收J(rèn)ira webhook請求,然后通過接口實(shí)現(xiàn)GitLab項(xiàng)目分支創(chuàng)建。
特性分支自動化:當(dāng)我們在jira上面創(chuàng)建了問題,此時會通過Jira的webhook觸發(fā)對應(yīng)的Jenkins作業(yè),該Jenkins作業(yè)通過解析Jira webhook傳遞的數(shù)據(jù),找到問題名稱和模塊名稱。調(diào)用GitlabAPI 項(xiàng)目查詢接口,根據(jù)模塊名稱找到代碼庫。調(diào)用GitLabAPI 分支創(chuàng)建接口,根據(jù)問題名稱基于主干分支創(chuàng)建一個特性分支。任務(wù)結(jié)束。
版本分支自動化:Jira創(chuàng)建發(fā)布版本,Issue關(guān)聯(lián)版本。自動在gitlab代碼庫基于master創(chuàng)建版本分支,并開啟特性分支到版本分支的合并請求。
2.1 準(zhǔn)備工作
在Jenkins, 創(chuàng)建一個Pipeline 作業(yè)并配置GenericTrigger 觸發(fā)器,接收J(rèn)iraWebhook數(shù)據(jù)。projectKey 參數(shù)表示Jira項(xiàng)目名稱,webHookData 參數(shù)為Jira ?webhook的所有數(shù)據(jù)。token 是觸發(fā)器的觸發(fā)token,這里默認(rèn)采用的作業(yè)名稱(作業(yè)名稱要唯一)。
triggers?{
????????GenericTrigger(?causeString:?'Trigger?By?Jira?Server?-->>>>>?Generic?Cause',?
????????????????????????genericRequestVariables:?[[key:?'projectKey',?regexpFilter:?'']],?
????????????????????????genericVariables:?[[defaultValue:?'',?key:?'webHookData',?regexpFilter:?'',?value:?'$']],?
????????????????????????printContributedVariables:?true,?
????????????????????????printPostContent:?true,?
????????????????????????regexpFilterExpression:?'',?
????????????????????????regexpFilterText:?'',?
????????????????????????silentResponse:?true,?
????????????????????????token:?"${JOB_NAME}"
????????)
????}
在Jira項(xiàng)目中配置Webhook,勾選觸發(fā)事件填寫觸發(fā)URL。http://jenkins.idevops.site/generic-webhook-trigger/invoke?token=demo-jira-service&projectKey=${project.key} ?(這個地址是jenkins Generictrigger生成的,這里不做過多的介紹)

Jira webhook數(shù)據(jù)參考, 這些參數(shù)可以在Jenkinsfile中通過readJSON格式化,然后獲取值。
response?=?readJSON?text:?"""${webHookData}"""
println(response)
//獲取webhook的事件類型
env.eventType?=?response["webhookEvent"]
{
????"timestamp":1603087582648,
????"webhookEvent":"jira:issue_created",
????"issue_event_type_name":"issue_created",
????"user":Object{...},
????"issue":{
????????"id":"10500",
????????"self":"http://192.168.1.200:8050/rest/api/2/issue/10500",
????????"key":"DEMO-2",
????????"fields":{
????????????"issuetype":{
????????????????"self":"http://192.168.1.200:8050/rest/api/2/issuetype/10001",
????????????????"id":"10001",
????????????????"description":"",
????????????????"iconUrl":"http://192.168.1.200:8050/images/icons/issuetypes/story.svg",
????????????????"name":"故事",
????????????????"subtask":false
????????????},
????????????"components":[
????????????????{
????????????????????"self":"http://192.168.1.200:8050/rest/api/2/component/10200",
????????????????????"id":"10200",
????????????????????"name":"demo-hello-service",
????????????????????"description":"demo-hello-service應(yīng)用"
????????????????}
????????????],
????????????"timespent":null,
????????????"timeoriginalestimate":null,
????????????"description":null,
????????????...
????????????...
????????????...
2.2 封裝GitLab接口
Gitlab接口文檔:https://docs.gitlab.com/ce/api/README.html
共享庫:src/org/devops/gitlab.groovy
package?org.devops
//封裝HTTP請求
def?HttpReq(reqType,reqUrl,reqBody){
????def?gitServer?=?"http://gitlab.idevops.site/api/v4"
????withCredentials([string(credentialsId:?'gitlab-token',?variable:?'gitlabToken')])?{
??????result?=?httpRequest?customHeaders:?[[maskValue:?true,?name:?'PRIVATE-TOKEN',?value:?"${gitlabToken}"]],?
????????????????httpMode:?reqType,?
????????????????contentType:?"APPLICATION_JSON",
????????????????consoleLogResponseBody:?true,
????????????????ignoreSslErrors:?true,?
????????????????requestBody:?reqBody,
????????????????url:?"${gitServer}/${reqUrl}"
????????????????//quiet:?true
????}
????return?result
}
//更新文件內(nèi)容
def?UpdateRepoFile(projectId,filePath,fileContent){
????apiUrl?=?"projects/${projectId}/repository/files/${filePath}"
????reqBody?=?"""{"branch":?"master","encoding":"base64",?"content":?"${fileContent}",?"commit_message":?"update?a?new?file"}"""
????response?=?HttpReq('PUT',apiUrl,reqBody)
????println(response)
}
//獲取文件內(nèi)容
def?GetRepoFile(projectId,filePath){
????apiUrl?=?"projects/${projectId}/repository/files/${filePath}/raw?ref=master"
????response?=?HttpReq('GET',apiUrl,'')
????return?response.content
}
//創(chuàng)建倉庫文件
def?CreateRepoFile(projectId,filePath,fileContent){
????apiUrl?=?"projects/${projectId}/repository/files/${filePath}"
????reqBody?=?"""{"branch":?"master","encoding":"base64",?"content":?"${fileContent}",?"commit_message":?"create?a?new?file"}"""
????response?=?HttpReq('POST',apiUrl,reqBody)
????println(response)
}
//更改提交狀態(tài)
def?ChangeCommitStatus(projectId,commitSha,status){
????commitApi?=?"projects/${projectId}/statuses/${commitSha}?state=${status}"
????response?=?HttpReq('POST',commitApi,'')
????println(response)
????return?response
}
//獲取項(xiàng)目ID
def?GetProjectID(repoName='',projectName){
????projectApi?=?"projects?search=${projectName}"
????response?=?HttpReq('GET',projectApi,'')
????def?result?=?readJSON?text:?"""${response.content}"""
????
????for?(repo?in?result){
???????//?println(repo['path_with_namespace'])
????????if?(repo['path']?==?"${projectName}"){
????????????
????????????repoId?=?repo['id']
????????????println(repoId)
????????}
????}
????return?repoId
}
//刪除分支
def?DeleteBranch(projectId,branchName){
????apiUrl?=?"/projects/${projectId}/repository/branches/${branchName}"
????response?=?HttpReq("DELETE",apiUrl,'').content
????println(response)
}
//創(chuàng)建分支
def?CreateBranch(projectId,refBranch,newBranch){
????try?{
????????branchApi?=?"projects/${projectId}/repository/branches?branch=${newBranch}&ref=${refBranch}"
????????response?=?HttpReq("POST",branchApi,'').content
????????branchInfo?=?readJSON?text:?"""${response}"""
????}?catch(e){
????????println(e)
????}??//println(branchInfo)
}
//創(chuàng)建合并請求
def?CreateMr(projectId,sourceBranch,targetBranch,title,assigneeUser=""){
????try?{
????????def?mrUrl?=?"projects/${projectId}/merge_requests"
????????def?reqBody?=?"""{"source_branch":"${sourceBranch}",?"target_branch":?"${targetBranch}","title":"${title}","assignee_id":"${assigneeUser}"}"""
????????response?=?HttpReq("POST",mrUrl,reqBody).content
????????return?response
????}?catch(e){
????????println(e)
????}
}
//搜索分支
def?SearchProjectBranches(projectId,searchKey){
????def?branchUrl?=??"projects/${projectId}/repository/branches?search=${searchKey}"
????response?=?HttpReq("GET",branchUrl,'').content
????def?branchInfo?=?readJSON?text:?"""${response}"""
????
????def?branches?=?[:]
????branches[projectId]?=?[]
????if(branchInfo.size()?==0){
????????return?branches
????}?else?{
????????for?(branch?in?branchInfo){
????????????//println(branch)
????????????branches[projectId]?+=?["branchName":branch["name"],
????????????????????????????????????"commitMes":branch["commit"]["message"],
????????????????????????????????????"commitId":branch["commit"]["id"],
????????????????????????????????????"merged":?branch["merged"],
????????????????????????????????????"createTime":?branch["commit"]["created_at"]]
????????}
????????return?branches
????}
}
//允許合并
def?AcceptMr(projectId,mergeId){
????def?apiUrl?=?"projects/${projectId}/merge_requests/${mergeId}/merge"
????HttpReq('PUT',apiUrl,'')
}
2.3 共享庫配置

?點(diǎn)擊屏末?|?閱讀原文?|?即刻學(xué)習(xí)