这篇主要系统的介绍一下 jenkins 的使用,这里jenkins是搭建在 windows上。
常用功能介绍
常用插件介绍
除了最开始安装jenkins时他推荐让你下载的插件,还有一些插件是需要我们自行下载的,所以在这part我就简单汇总下其他我们可能要用到的一些插件。
- 注意: jenkins上自行下载的一些插件,需要重启jenkins后才生效;
- 另外像jdk,maven这些插件必须要自行在jenkins上配置好环境变量才可以运用。
因为jenkins下载了插件只代表你目前的jenkins能够支持使用这个插件,但是具体的插件位置其实是你本地的,而环境变量的配置就是去找你本地的目录。
另外常用的插件介绍
- Rebuilder: 此插件可以直接重复上次构建
- Pipeline: 持续交付插件,可以在新增 job时选择这一类型插件,然后通过写pipeline代码去运行job
- Blue Ocean: 蓝海,可可视化看到任务的状态
- Allure : 使用allure生成测试报告
- robotframework: jenkins集成robotframework
创建一个自由风格的Job
我这里只是简单介绍一个简单任务的创建,大家可以按照各自需求配置自己的任务。
Jenkins新增节点
一般情况下,我们都不会在master节点上面去运行任务,通常会新增slave节点运行,由于我jenkins搭建在windows上,我这里就简单写下windows节点的新增。
新增windows节点
启动windows节点
在新节点上运行任务
Jenkins 配置报警机制
流程简单来说:
- 就是在jenkins中配置好email后,
- 运行任务时我们添加邮件触发器,当任务失败或者成功时,自动发送邮件
配置Email
下载Email相关插件
- Email Extension
- Email Extension Template Plugin
这两插件的作用即:帮助用户方便的设置格式化邮件。
配置管理员邮箱
配置邮件通知
配置邮件模板
Jenkins可以根据你配置的邮件模板格式发送结果邮件,通过Jenkins的参数定制自己的Email模板,常用的参数key值如下。
# 常用参数
$BUILD_STATUS -构建结果
$PROJECT_NAME -构建脚本名称
$BUILD_NUMBER -构建脚本编号
$JOB_DESCRIPTION -构建项目描述
$CAUSE - 脚本启动原因
$BUILD_URL - 脚本构建详情URL地址
模板内容,可以自行写
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title> </head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0"
style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>本邮件是Jenkins自动发送,请勿回复!</td>
</tr>
<tr>
<td><h3>
<font color="#e53935">    构建结果 - ${BUILD_STATUS}!</font>
</h3></td>
</tr>
<tr>
<td><br />
<b><font color="#3f51b5">构建信息:</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>项目名称 : ${PROJECT_NAME}</li>
<li>构建编号 : 第${BUILD_NUMBER}次构建</li>
<li>触发原因: ${CAUSE}</li>
<li>构建日志: <a href="{BUILD_URL}console">{BUILD_URL}console</a></li>
<li>构建 Url : <a href="{BUILD_URL}">{BUILD_URL}</a></li>
<li>工作目录 : <a href="{PROJECT_URL}ws">{PROJECT_URL}ws</a></li>
<li>项目 Url : <a href="{PROJECT_URL}">{PROJECT_URL}</a></li>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></a></li>
</ul>
</td>
<tr>
<td><b><font color="#3f51b5">构建日志:</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><textarea cols="160" rows="80" readonly="readonly"
style="font-family: Microsoft YaHei">${BUILD_LOG,maxLines=1000}</textarea>
</td>
</tr>
</html>
任务配置触发器
配置任务触发器
给你想要添加报警机制的任务添加即可
执行任务,查看邮件
Jenkins配置父子job
父子Job简要介绍
正常情况下,我们通常会有很多个任务,然后这些任务之间是有衔接的,比如先要执行一个任务,且这个任务是成功的状态下再去执行另一个任务
- 适用场景:有先后次序关系的任务
- 举个简单的例子:当我们要实现UI自动化时,首先我们需要先将最新的APP包给下载下来,下载成功后我们再去运行UI自动化用例
任务启动的触发条件:其他任务的运行结果
那下面我分别来详细讲下这三种情况的具体操作
- 1、前驱任务成功的条件下被触发
- 2、前驱任务失败的条件下被触发
- 3、前驱任务不稳定的条件下被触发
父Job成功的条件下被触发
父Job失败的条件下被触发
- 这里不重新再创建新任务,只在原任务上稍微调整一下
父Job不稳定的条件下被触发
- 一样的,我这里也还是在原有任务上面进行简单修改,实现这种场景
- 这里用到了一个新的插件Text Find,之前统一让大家下载过,如果没有下载过的话自行先下载下这个插件
JenkinsAPI 接口调用
Jenkins_API简要介绍
Jenkins_API:即Jenkins对外暴露的动作交互入口,为外部程序提供入口,可以控制Jenkins。
- 支持协议:Http
- 常用功能:运行Job,查看任务状态,返回任务编号
request调用JenkinsAPI
这里简单介绍下request进行调用JenkinsAPI,详细看下注释,其实很简单。
"""
该类调用jenkins_api接口
1、获取任务的最新编号
2、获取任务的详细信息
"""
import jsonimport requests
注意:这个地址前面部分
mikasa:yy1998123 是你的jenkins用户名和密码
127.0.0.1:8080 是本地jenkins域名+端口号
url = "http://mikasa:yy1998123@127.0.0.1:8080/jenkins/job/"
def get_jenkins_url(job_name):
"""
拼接url+任务job
:return:
"""
# print("拼接url为:", url + job_name)
return url + job_namedef send_api(req, tools="requests"):
"""
对发送接口测试的工具进行封装(可以使用urlib3/requests)
:param tools:
:param req:
:return:
"""
if tools == "requests":
return requests.request(**req)def get_latest_job_number():
"""
1、获取最新任务编号
:return:
"""
req = {
"method": "GET",
"url": get_jenkins_url("mikasa_demo001") + "/lastBuild/buildNumber",
}
res = send_api(req)
print("最新任务编号:", res.json())def get_job_info():
"""
2、获取job详细信息
:return:
"""
req = {
"method": "GET",
"url": get_jenkins_url("mikasa_demo001") + "/api/json",
}
res = send_api(req)
res_json = json.dumps(res.json(), indent=2)
print("返回结果:", res_json)
get_latest_job_number()
get_job_info()
使用jenkins api库调用
-这里介绍下利用jenkins api库去调用,上面是我们自己去写request请求去调用,而其实目前已经存在了轮子,我们直接使用Jenkins api即可。
下载jenkinsapi库
封装jenkins调用
- jenkins_api.py
"""
封装jenkins调用类
"""
import configparser
import datetime
import logging
import os
import re
from jenkinsapi.jenkins import Jenkinslogging.basicConfig(level=logging.INFO, format='[%(asctime)s] - [%(name)s] - [%(levelname)s] - [%(message)s]')
log = logging.getLogger(__name__)def get_jenkins_config(chose):
"""
读取Jenkins配置:从配置文件中jenkins_server.ini
:param chose:
:return:
"""
config = configparser.ConfigParser()
# 读取配置
config.read(os.path.join(os.getcwd(), 'jenkins_server.ini'))
username = config.get(chose, 'username')
password = config.get(chose, 'password')
host = config.get(chose, 'host')
port = config.get(chose, 'port')
url = "http://" + host + ":" + port
return url, username, passwordclass JenkinsDemo:
def init(self, job_name, chose='jenkins'):
"""
初始化,拿到jenkins配置
:param job_name:
:param chose:
"""
self.job_name = job_name
config = get_jenkins_config(chose)
print("config:", config)
# 解包元祖
self.jk = Jenkins(*config, useCrumb=True)def __get_job_from_keys(self): """ 拿到所有的job名称 :return: 返回一个列表 """ choose_list = [] print(self.jk.keys()) for my_job_name in self.jk.keys(): # 遍历拿到所有的job,判断当前job是否在job列表里面,在的话添加到自定义列表 if self.job_name in my_job_name: choose_list.append(my_job_name) return choose_list def __job_build(self, my_job_name): """ 构建job :param my_job_name: :return: """ if self.jk.has_job(my_job_name): # 如果有这个job拿到他里面的job对象 my_job = self.jk.get_job(my_job_name) if not my_job.is_queued_or_running(): # 如果job当前没有在运行的话,就运行 try: # 若当前没有在跑的话,拿到最后一次构建数 last_build = my_job.get_last_buildnumber() except: # 若没有获取到最后一次构建数的话,默认置为0 last_build = 0 # 最新构建数+1 build_num = last_build + 1 try: # 开始打包 self.jk.build_job(my_job_name) except Exception as e: log.error(str(e)) # 循环判断Jenkins是否打包完成 while True: # 若当前任务没有运行才获取信息 if not my_job.is_queued_or_running(): # 拿到最新一次的大奥信息 count_build = my_job.get_build(build_num) # 获取打包开始时间 start_time = count_build.get_timestamp() + datetime.timedelta(hours=8) # 获取打包日志 console_out = count_build.get_console() # 获取打包状态 status = count_build.get_status() # 获取变更内容 change = count_build.get_changeset_items() log.info(" " + str(start_time) + " 发起的" + my_job_name + "构建已经完成,构建的状态为:" + status) p2 = re.compile(r".*ERROR.*") err_list = p2.findall(console_out) log.info("打包日志为:" + str(console_out)) if status == "SUCCESS": if len(change) > 0: for data in change: for file_list in data["affectedPaths"]: log.info("发起的" + my_job_name + "变更的类:" + file_list) log.info("发起的" + my_job_name + "变更的备注:" + data["msg"]) log.info("发起的" + my_job_name + "变更的提交人:" + data["author"]["fullName"]) else: log.info("发起的" + my_job_name + "构建没有变更内容") if len(err_list) > 0: log.warning("构建的" + my_job_name + "构建状态为成功,但包含了以下错误:") for error in err_list: log.error(error) else: if len(err_list) > 0: log.warning("构建的" + my_job_name + "包含了以下错误:") for error in err_list: log.error(error) break else: log.warning("发起的" + my_job_name + "Jenkins is running") else: log.warning("发起的" + my_job_name + "没有该服务") def run(self): my_job_name = self.__get_job_from_keys() if len(my_job_name) == 1: self.__job_build(my_job_name[0]) elif len(my_job_name) == 0: log.error("输入的job名称不正确!")
if name == 'main':
jk = JenkinsDemo("mikasa_demo001")
jk.run()
- jenkins_server.ini
[jenkins]
username=mikasa
password=yy1998123
host=127.0.0.1
port=8080
Jenkins多线程任务执行
正常一个项目部署中,为了节省时间,我们通常都可以将一些没有依赖关系的任务同步执行。
- 比如说:在进行UI自动化中,下载app包的同时,我们可以把git源码同步更新给拉下来
那本章就简单讲一个例子,多线程的情况下我们如何去写jenkinsfile。
任务示例
- 代码我已上传到github:https://github.com/Burden1/Mikasa_pipeline_demo
mikasa_parallel_demo
- 并行任务即在外面加个**parallel{}**,里面包含stage{}即可。
pipeline {
agent none
stages {
stage('run parallel Stage') {
parallel {
stage('mikasa_Stage_1') {
agent { label "slave" }
steps {
echo "at agent slave run task 1."
bat "ipconfig"
sleep 10
}
}
stage('mikasa_Stage_2') {
agent { label "master" }
steps {
echo "at agent master run task 2."
bat "ipconfig"
sleep 10
}
}
}
}
}
}
_10722865.html