一、系统环境
组件 | 版本 |
---|---|
Ubuntu | 20.04 |
Jenkins | 2.319.1 |
Bitbucket |
二、相关资料
三、简单入门
Jenkins Pipeline 提供了一套可扩展的工具,用于将“简单到复杂”的交付流程实现为“持续交付即代码”。
Pipeline包含声明式和脚本式流水线都是 DSL 语言,用来描述软件交付流水线的一部分。
脚本式流水线是用一种限制形式的 Groovy 语法编写的,Groovy语法与Java语法类似,对Java开发人员友好,甚至有书直言会Java就会Groovy~
流水线创建方式包括:经典UI、BlueOcean、Jenkinsfile
官方教程-经典UI
官方教程-BlueOcean
官方教程-Jenkinsfile
官方教程-流水线语法
四、简单示例
全局变量文档 jenkins地址/pipeline-syntax/globals
官方教程-钉钉机器人
官方教程-build user vars插件
pipeline {
//代理,通常是一个机器或容器
agent any
//环境变量,类似全局变量
environment {
//构建执行者
BUILD_USER = ""
}
//构建触发器,Jenkins自动构建条件
triggers{
//每3分钟判断一次代码是否有变化
pollSCM('H/3 * * * *')
}
stages {
//构建阶段
stage('Build') {
//使用build user vars插件,获取构建执行者
steps {
wrap([$class: 'BuildUser']) {
script {
//将构建执行者注入到环境变量中,方便最后通知使用
BUILD_USER = "${env.BUILD_USER}"
}
}
/* 从Bitbucket上拉取分支
* @url git地址
* @branch 分支名称
* @credentialsId Jenkins凭证Id,用于远程访问
*/
git(url: 'https://demo@bitbucket.org/demo/demo.git', branch: 'master', credentialsId: 'Bitbucket')
//执行maven打包
//-B --batch-mode 在非交互(批处理)模式下运行(该模式下,当Mven需要输入时,它不会停下来接受用户的输入,而是使用合理的默认值);
//打包时跳过JUnit测试用例,
//-DskipTests 不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下
//-Dmaven.test.skip=true,不执行测试用例,也不编译测试用例类
sh 'mvn -B -DskipTests clean package'
}
}
//部署阶段
stage('Deliver') {
steps {
//解决杀掉了所有子进程问题
withEnv(['JENKINS_NODE_COOKIE=background_job']) {
//执行脚本
sh '''
# 服务器名称(pom文件定义)
SERVER_NAME=police-tools
# 生成的jar包名称
JAR_NAME=${SERVER_NAME}-0.0.1-SNAPSHOT.jar
# 分支名称
BRANCH=master
# 生成的jar包路径
JAR_PATH=/var/lib/jenkins/workspace/${SERVER_NAME}/target
# 运行jar的工作路径,统一管理,并需要提前创建好
JAR_WORK_PATH=/usr/local/jenkins/maven/jar/${SERVER_NAME}/${BRANCH}
# 停止服务并杀死进程
pid=$(ps -ef | grep ${JAR_NAME} | grep -v grep | awk '{ print $2 }')
if [ -z "$pid" ]
then
echo ${JAR_NAME} is already stopped
else
echo kill ${JAR_NAME}
echo kill -15 ${pid}
kill -15 ${pid}
fi
# kill需要一定时间,等待10秒,线上环境请增加判断
sleep 10
# 创建默认路径
mkdir -p ${JAR_WORK_PATH}
# 移动打包文件
cp -f ${JAR_PATH}/${JAR_NAME} ${JAR_WORK_PATH}
# 移动到工作目录下,否则log日志会打印到当期工作目录
cd ${JAR_WORK_PATH}
# /dev/null 所有写入它的内容都会永远丢失
nohup java -jar ${JAR_WORK_PATH}/${JAR_NAME} >/dev/null 2>>${JAR_WORK_PATH}/sys_error.log &
'''
}
}
}
}
//根据流水线或阶段的完成情况而运行,包含always、changed、failure、success、unstable、aborted状态
post {
//成功
success {
//DingTalk插件,钉钉通知
dingtalk (
//机器人ID
robot: "dev1_robot",
//通知类型,整体跳转
type:'ACTION_CARD',
//通知所有人,如果想要通知一些人则可用at
atAll: false,
//标题
title: "构建成功:${env.JOB_NAME}",
//通知内容
text: [
"### [${env.JOB_NAME}](${env.JOB_URL}) ",
'---',
"- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
'- 状态:<font color=#00CD00 >成功</font>',
"- 持续时间:${currentBuild.durationString}".split("and counting")[0],
"- 执行人:${BUILD_USER}",
"- 提交日志: ${changeString}",
]
)
}
failure{
dingtalk (
robot: "dev1_robot",
type:'ACTION_CARD',
atAll: false,
title: "构建失败:${env.JOB_NAME}",
//messageUrl: 'xxxx',
text: [
"### [${env.JOB_NAME}](${env.JOB_URL}) ",
'---',
"- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
'- 状态:<font color=#EE0000 >失败</font>',
"- 持续时间:${currentBuild.durationString}".split("and counting")[0],
"- 执行人:${BUILD_USER}",
"- 提交日志: ${changeString}",
]
)
}
}
}
//从changeSets中格式化获取提交日志
@NonCPS
def getChangeString() {
MAX_MSG_LEN = 100
# 提交日志
def changeString = ""
echo "Gathering SCM changes"
def changeLogSets = currentBuild.changeSets
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
truncated_msg = entry.msg.take(MAX_MSG_LEN)
changeString += " -${entry.author} : ${truncated_msg} \n"
}
}
if (!changeString) {
changeString = " - No new changes"
}
return changeString
}