2、Jira自动化实践
Jira自动化实践
目录
[toc]
推荐文章
https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》
1、Jira简介
中文官网: https://www.atlassian.com/zh/software/jira
==Jira可以做什么?==
规划
创建项目,用户需求和事务、规划 Sprint 并跨团队分配开发任务。
跟踪
全面了解项目进度情况,安排整个团队工作的优先级排序并进行讨论。
2、Jira使用实践
创建一个项目
注意: 一个Jira 项目对应一个GitLab项目组;
为项目添加模块
注意: 一个Jira模块对应一个GitLab项目;
配置WebHook
系统, 网络钩子(webhook)
需求/任务管理
创建需求
这里在Jira上面把这个需求add login page
, 类型为任务
关联到devops6-maven-service
模块;
发布Release
课程中把 发布对应为GitLab项目代码库中的版本分支;
issue关联发布: 可以想象成GitLab特性分支合并到版本分支;
3、Jira自动化实践
工作流
工具链集成
- 实验 环境
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
特性分支
提交一次代码。
- 创建版本分支
RELEASE-10.1.1
- 将特性分支合并到版本分支
这个是手动操作,接下来,我们就利用Jira,jenkins,gitlab来完成操作。
1、需求与代码关联
1.创建Jenkins作业并配置webhook
- 配置
Generic Webhook Trigger
webhookData: 这个变量存放的是Jira 传递的数据。
2.为Jira配置一个系统级别的webhook
选项解释:
- 指定Jenkins的webhook触发器地址;
- 通过JQL指定,允许devops6这个项目进行触发;
- 勾选动作,触发事件;(我在jira做了什么操作之后能够触发)
- 此时,我们来测试下
在jira上创建一个需求,看是否会触发jenkins作业?
可以看到jenkins作业被成功触发了。
我们再来打印下这个${webhookData}
变量:
编辑下pipeline代码,重新构建:
- 我们把json数据拿到在线json解析下
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}"
把此部分代码放到流水线里,重新构建下,可看到如下效果:
- 封装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, 关联 项目;
- Jenkins 流水线运行;
- 验证Gitlab中多了分支;
符合预期。
2、代码自动化合并
1. 更新配置Jira Webhook
2. Issue关联版本
- 这里有2个待关联版本的问题
- 创建
11.1.1
发布版本
同时可以看下devops6-maven-service
仓库是没有这个11.1.1
版本分支的。
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上关联问题到发布版本
可以看到触发了jenkins流水线:
gitlab上也能看到有mr请求了:
jira付费插件也能支持这样的效果。
测试结束。😘
FAQ
ones
关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码 x2675263825 (舍得), qq:2675263825。
🍀 微信公众号 《云原生架构师实战》
🍀 语雀
https://www.yuque.com/xyy-onlyone
🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
🍀 知乎 https://www.zhihu.com/people/foryouone
最后
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!