跳到主要内容

2、Jira自动化实践

Jira自动化实践

image-20230712071753069

目录

[toc]

推荐文章

https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》

image-20230624094222589

1、Jira简介

image-20230712071158183

中文官网: https://www.atlassian.com/zh/software/jira

tstmp_20230709214731

==Jira可以做什么?==

规划

创建项目,用户需求和事务、规划 Sprint 并跨团队分配开发任务。

跟踪

全面了解项目进度情况,安排整个团队工作的优先级排序并进行讨论。

image-20230709214858649

2、Jira使用实践

创建一个项目

注意: 一个Jira 项目对应一个GitLab项目组;

image-20230710071324180

image-20230710071342437

image-20230710071421815

image-20230710071527852

为项目添加模块

注意: 一个Jira模块对应一个GitLab项目;

image-20230710071710949

配置WebHook

系统, 网络钩子(webhook)

tstmp_20230710071735

需求/任务管理

创建需求

这里在Jira上面把这个需求add login page, 类型为任务 关联到devops6-maven-service模块;

image-20230710072634191

image-20230710072658373

发布Release

课程中把 发布对应为GitLab项目代码库中的版本分支;

image-20230710072750413

issue关联发布: 可以想象成GitLab特性分支合并到版本分支;

image-20230710072904046

image-20230710072922424

3、Jira自动化实践

工作流

image-20230711094720883

工具链集成

image-20230711094833711

  • 实验环境
gitlab-ce:15.0.3-ce.0
jenkins:2.346.3-2-lts-jdk11
atlassian-jira-software-9.4.8-x64
  • 实验软件

pipeline见正文。

==gitlab上模拟手动创建分支,提交代码==

  • 创建DEVOPS6-1特性分支

image-20230710073225129

提交一次代码。

  • 创建版本分支RELEASE-10.1.1

image-20230710073348020

  • 将特性分支合并到版本分支

image-20230710073432996

这个是手动操作,接下来,我们就利用Jira,jenkins,gitlab来完成操作。

1、需求与代码关联

image-20230711095313949

1.创建Jenkins作业并配置webhook

image-20230711095634664

  • 配置Generic Webhook Trigger

webhookData: 这个变量存放的是Jira 传递的数据。

image-20230711100047538

image-20230711100120530

image-20230711100133187

2.为Jira配置一个系统级别的webhook

image-20230711100550141

选项解释:

  1. 指定Jenkins的webhook触发器地址;
  2. 通过JQL指定,允许devops6这个项目进行触发;
  3. 勾选动作,触发事件;(我在jira做了什么操作之后能够触发)
  • 此时,我们来测试下

在jira上创建一个需求,看是否会触发jenkins作业?

image-20230711100900876

可以看到jenkins作业被成功触发了。

image-20230711100929592

我们再来打印下这个${webhookData}变量:

编辑下pipeline代码,重新构建:

image-20230711101201728

  • 我们把json数据拿到在线json解析下

image-20230711101905585

image-20230711101338622

image-20230711101419150

image-20230711102033403

3.Jenkins流水线配置

  • 解析Jira传递过来的数据;
webhookData = readJSON text: "${webhookData}"


// Jira事件
jiraEvent = webhookData.webhookEvent
jiraProjectName = webhookData.issue.fields.project.name

// 获取gitlab参数
gitlabProjects = []
gitlabBranchName = webhookData.issue.key
gitlabGroupName = jiraProjectName

for (i in webhookData.issue.fields.components){
gitlabProjects.add(i["name"])
}

currentBuild.description = "Trigger by ${jiraEvent} \n project: ${gitlabProjects} \n branch: ${gitlabBranchName}"

把此部分代码放到流水线里,重新构建下,可看到如下效果:

image-20230711102313235

  • 封装GitLab API接口

https://docs.gitlab.com/ee/api/branches.html#create-repository-branch

// 创建分支
def CreateBranch(projectId, newBranchName, sourceBranchName){
try {
apiUrl = "projects/${projectId}/repository/branches?branch=${newBranchName}&ref=${sourceBranchName}"
response = HttpReq('POST', apiUrl, "")
}
catch(Exception e) {
println(e)

}
}

// 获取所有项目的id
def GetProjectsId(gitlabGroupName, gitlabProjects){
gitlabProjectIds = []
for (project in gitlabProjects){
id = GetProjectId(gitlabGroupName, project)
println(id)
if (id != 0){
gitlabProjectIds.add(id)
}
}
return gitlabProjectIds
}

// 根据项目名称获取项目id
def GetProjectId(groupName, projectName){
apiUrl = "projects?search=${projectName}"
response = HttpReq('GET', apiUrl, "")
response = readJSON text: response.content - "\n"

if (response.size() > 1){
for (i in response){
println(i["path_with_namespace"])
println(groupName + projectName)
if (i["path_with_namespace"] == "${groupName}/${projectName}"){
println(i["id"])
return i["id"]
}
}
} else {
return response[0]["id"]
}
}


// 封装HTTP
def HttpReq(reqType, reqUrl,reqBody ){
def gitServer = "http://192.168.1.200/api/v4"
withCredentials([string(credentialsId: '058b7907-ebe2-4d14-9b91-1ac72e071c59', variable: 'GITLABTOKEN')]) {
response = httpRequest acceptType: 'APPLICATION_JSON_UTF8',
consoleLogResponseBody: true,
contentType: 'APPLICATION_JSON_UTF8',
customHeaders: [[maskValue: false, name: 'PRIVATE-TOKEN', value: "${GITLABTOKEN}"]],
httpMode: "${reqType}",
url: "${gitServer}/${reqUrl}",
wrapAsMultipart: false,
requestBody: "${reqBody}"

}
return response
}
  • Pipeline主程序
pipeline {
agent { label "build" }

stages{
stage("Process"){
steps{
script{
println(gitlabProjects)
println(gitlabBranchName)
projectIds = GetProjectsId(gitlabGroupName, gitlabProjects)

switch(jiraEvent) {
case "jira:issue_created":
println(projectIds)
for (id in projectIds){
CreateBranch(id, gitlabBranchName, "main")
}
break
default:
println("error...")
break
}
}
}
}
}
}
  • 完整代码
webhookData = readJSON text: "${webhookData}"


// Jira事件
jiraEvent = webhookData.webhookEvent
jiraProjectName = webhookData.issue.fields.project.name

// 获取gitlab参数
gitlabProjects = []
gitlabBranchName = webhookData.issue.key
gitlabGroupName = jiraProjectName

for (i in webhookData.issue.fields.components){
gitlabProjects.add(i["name"])
}

currentBuild.description = "Trigger by ${jiraEvent} \n project: ${gitlabProjects} \n branch: ${gitlabBranchName}"


pipeline {
agent { label "build" }

stages{
stage("Process"){
steps{
script{
println(gitlabProjects)
println(gitlabBranchName)
projectIds = GetProjectsId(gitlabGroupName, gitlabProjects)

switch(jiraEvent) {
case "jira:issue_created":
println(projectIds)
for (id in projectIds){
CreateBranch(id, gitlabBranchName, "main")
}
break
default:
println("error...")
break
}
}
}
}
}
}


// 创建分支
def CreateBranch(projectId, newBranchName, sourceBranchName){
try {
apiUrl = "projects/${projectId}/repository/branches?branch=${newBranchName}&ref=${sourceBranchName}"
response = HttpReq('POST', apiUrl, "")
}
catch(Exception e) {
println(e)

}
}

// 获取所有项目的id
def GetProjectsId(gitlabGroupName, gitlabProjects){
gitlabProjectIds = []
for (project in gitlabProjects){
id = GetProjectId(gitlabGroupName, project)
println(id)
if (id != 0){
gitlabProjectIds.add(id)
}
}
return gitlabProjectIds
}

// 根据项目名称获取项目id
def GetProjectId(groupName, projectName){
apiUrl = "projects?search=${projectName}"
response = HttpReq('GET', apiUrl, "")
response = readJSON text: response.content - "\n"

if (response.size() > 1){
for (i in response){
println(i["path_with_namespace"])
println(groupName + projectName)
if (i["path_with_namespace"] == "${groupName}/${projectName}"){
println(i["id"])
return i["id"]
}
}
} else {
return response[0]["id"]
}
}


// 封装HTTP
def HttpReq(reqType, reqUrl,reqBody ){
def gitServer = "http://172.29.9.101/api/v4"
withCredentials([string(credentialsId: '5782c77d-ce9d-44e5-b9ba-1ba2097fc31d', variable: 'GITLABTOKEN')]) {
response = httpRequest acceptType: 'APPLICATION_JSON_UTF8',
consoleLogResponseBody: true,
contentType: 'APPLICATION_JSON_UTF8',
customHeaders: [[maskValue: false, name: 'PRIVATE-TOKEN', value: "${GITLABTOKEN}"]],
httpMode: "${reqType}",
url: "${gitServer}/${reqUrl}",
wrapAsMultipart: false,
requestBody: "${reqBody}"

}
return response
}

将次代码放到pipeline里。

4.效果验证

  • 在Jira里面创建一个issue, 关联 项目;

image-20230711103206398

  • Jenkins 流水线运行;

image-20230711103508529

  • 验证Gitlab中多了分支;

image-20230711103537620

符合预期。

2、代码自动化合并

image-20230712064825091

1. 更新配置Jira Webhook

image-20230712065003182

2. Issue关联版本

  • 这里有2个待关联版本的问题

image-20230712065513067

  • 创建11.1.1发布版本

image-20230712065205538

同时可以看下devops6-maven-service仓库是没有这个11.1.1版本分支的。

image-20230712065455878

3. Jenkins Pipeline

  • 分析Jira Webhook传递的数据
jira event : 
webhookEvent: jira:issue_updated


args:
1. gitlab 项目名称 issue.fields.components []
2. 分支名称 issue.key
3. gitlab 项目组名称 issue.fields.project.name
4. fixversion 版本分支 issue.fields.fixVersions []


action:
1.根据项目名称获取项目的id;
2.根据项目id,基于master分支创建一个版本分支;
3.根据fixversion, 拿到所有的特性分支;
4.根据项目id,将该项目的特性分支合并版本分支;
  • 编写Jenkinsfile
webhookData = readJSON text: "${webhookData}"

// Jira事件
jiraEvent = webhookData.webhookEvent
jiraProjectName = webhookData.issue.fields.project.name

// 获取gitlab参数
gitlabProjects = []
gitlabBranchName = webhookData.issue.key
gitlabGroupName = jiraProjectName

for (i in webhookData.issue.fields.components){
gitlabProjects.add(i["name"])
}

currentBuild.description = "Trigger by ${jiraEvent} \n project: ${gitlabProjects} \n branch: ${gitlabBranchName}"

pipeline {
agent { label "build" }

stages{
stage("Process"){
steps{
script{
println(gitlabProjects)
println(gitlabBranchName)
projectIds = GetProjectsId(gitlabGroupName, gitlabProjects)
switch(jiraEvent) {
case "jira:issue_created":
println(projectIds)
for (id in projectIds){
CreateBranch(id, gitlabBranchName, "main")
}
break
case "jira:issue_updated":
if (webhookData.issue.fields.fixVersions.size() >= 1){
jiraFixVersion = webhookData.issue.fields.fixVersions[0]["name"]
// 获取fixversion关联的所有issues
issues = GetIssuesByFixVersion(jiraProjectName, jiraFixVersion)

// 在issue关联的所有项目创建版本分支
for (id in projectIds){
CreateBranch(id, "RELEASE-${jiraFixVersion}", "master") //RELEASE-1.1.6

// 创建合并请求 特性分支 > 版本分支
for(issue in issues) {
CreateMergeRequest(id, issue, "RELEASE-${jiraFixVersion}" )
}
}
break
}
default:
println("error...")
break
}
}
}
}
}
}

//创建合并请求
def CreateMergeRequest(projectId, sourceBranch, targetBranch ){
try {
apiUrl = "projects/${projectId}/merge_requests"
reqBody = """{"source_branch": "${sourceBranch}","target_branch":"${targetBranch}","title": "${sourceBranch}>>>${targetBranch}byJenkins"}"""
println(reqBody)
response = HttpReq('POST',apiUrl,reqBody)
}
catch(Exception e) {
println(e)
}
}

// 查询JiraReleaseissue
def GetIssuesByFixVersion(projectName, fixVersion){
jql = "project%20=%20${projectName}%20AND%20fixVersion%20=%20${fixVersion}"
response = sh returnStdout: true, script: """
curl \
-u admin:Admin@123 \
-H "Content-Type: application/json" \
--request GET "http://172.29.9.101:8066/rest/api/2/search?jql=${jql}" -s
"""
response = readJSON text: """ ${response - "\n"} """
issues = []
for (i in response["issues"]){
issues.add(i["key"])
}

return issues
}

// 创建分支
def CreateBranch(projectId, newBranchName, sourceBranchName){
try {
apiUrl = "projects/${projectId}/repository/branches?branch=${newBranchName}&ref=${sourceBranchName}"
response = HttpReq('POST', apiUrl, "")
}
catch(Exception e) {
println(e)

}
}

// 获取所有项目的id
def GetProjectsId(gitlabGroupName, gitlabProjects){
gitlabProjectIds = []
for (project in gitlabProjects){
id = GetProjectId(gitlabGroupName, project)
println(id)
if (id != 0){
gitlabProjectIds.add(id)
}
}
return gitlabProjectIds
}

// 根据项目名称获取项目id
def GetProjectId(groupName, projectName){
apiUrl = "projects?search=${projectName}"
response = HttpReq('GET', apiUrl, "")
response = readJSON text: response.content - "\n"

if (response.size() > 1){
for (i in response){
println(i["path_with_namespace"])
println(groupName + projectName)
if (i["path_with_namespace"] == "${groupName}/${projectName}"){
println(i["id"])
return i["id"]
}
}
} else {
return response[0]["id"]
}
}


// 封装HTTP
def HttpReq(reqType, reqUrl,reqBody ){
def gitServer = "http://172.29.9.101:8076/api/v4"
withCredentials([string(credentialsId: '5782c77d-ce9d-44e5-b9ba-1ba2097fc31d', variable: 'GITLABTOKEN')]) {
response = httpRequest acceptType: 'APPLICATION_JSON_UTF8',
consoleLogResponseBody: true,
contentType: 'APPLICATION_JSON_UTF8',
customHeaders: [[maskValue: false, name: 'PRIVATE-TOKEN', value: "${GITLABTOKEN}"]],
httpMode: "${reqType}",
url: "${gitServer}/${reqUrl}",
wrapAsMultipart: false,
requestBody: "${reqBody}"

}
return response
}

4. 效果验证

  • jira上关联问题到发布版本

image-20230712070029427

image-20230712070822965

可以看到触发了jenkins流水线:

image-20230712070908063

gitlab上也能看到有mr请求了:

image-20230712070918429

jira付费插件也能支持这样的效果。

测试结束。😘

FAQ

ones

image-20230712071443572

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

image-20230107215114763

🍀 微信公众号 《云原生架构师实战》

image-20230107215126971

🍀 语雀

https://www.yuque.com/xyy-onlyone

image-20230624093747671

🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

image-20230107215149885

🍀 知乎 https://www.zhihu.com/people/foryouone

image-20230107215203185

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!