在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:jenkins-json-build开源软件地址:https://gitee.com/beijing-guangyu-online/jenkins-json-build开源软件介绍:Jenkins Json Build我所在的组织项目数量众多,使用的语言和框架也很多,比如Java、ReactNative、C# .NET、Android、iOS等,部署环境也是多种多样比如Tomcat、K8S、IIS、客户端应用是局域网内企业证书安装等,我们没有专门的配置管理员或构建部署专员,都是开发人员自己在Jenkins中写构建脚本,每个项目都有自己的构建脚本(Scripted Pipelines),但类型相同的项目比如都是Java或都是.NET项目之间,构建脚本其实都很类似,都是靠几个已存在的构建脚本改写出来的,其实开发人员对编写Jenkins构建脚本了解也不多,另外因为没有规则和约束,更没有代码复用的机制,构建部署工作很混乱和难以管理。 在上述情况下我们开发了Jenkins-Json-Build项目,该项目适合于有一些编程经验的人员在不需要了解Jenkins构建脚本如何编写的情况下,通过简单的配置Json文件,就可以轻松完成一个项目的获取源码、单元测试、代码检查、编译构建、部署等步骤,实现一个典型的CI过程,又因为此项目使用了Jenkins共享类库(Shared Libraries)机制,构建脚本复用率得到了大幅度提高,并且开发人员可以方便的扩展更多的功能,满足不同构建部署场景的需要,此项目非常适合那些开发人员自己管理构建部署的团队,通过Jenkins-Json-Build项目组织对构建部署过程进行了统一的管理和监督,又让每个项目有足够的灵活性和自主权满足各自项目构建部署的特殊性。 内容列表
准备工作创建Jenkins流水线任务依赖插件新建流水线在Jenkins系统中新建一个流水线任务:创建完成后:
最后保存退出。 项目目录中需要的文件项目目录下需要两个文件,Jenkinsfile和jenkins-project.json,可以从【示例文件】获得,其中Jenkinsfile是Jenkins在获得项目源码之后自动执行的构建脚本文件,jenkins-project.json是本项目需要的构建配置文件(jenkins-project.json是默认的json构建配置文件名称也可以在脚本中修改为其他文件名称)。 配置共享类库在系统配置中需要对共享类库进行设置,共享类库设置如下:shared-library是共享类库的名字,以后在构建脚本中会用到,项目仓库中是共享类库的仓库地址。将本项中shared-library目录下的resources、src、vars三个目录(类库源码)拷贝到共享类库的仓库中。 运行流水线任务新建流水线任务完成后,点击立即构建,完成构建过程: Json文档格式及运行方式上述示例中构建分为5个阶段:初始化、代码检查、单元测试、编译构建、部署,构建脚本如下(以下是Declarative方式,也可转换为Scripted方式): @Library('shared-library') _pipeline { agent any stages { stage('初始化') { steps { script{ runWrapper.loadJSON('/jenkins-project.json') runWrapper.runSteps('初始化') } } } stage('代码检查') { steps { script{ runWrapper.runSteps('代码检查') } } } stage('单元测试') { steps { script{ runWrapper.runSteps('单元测试') } } } stage('编译构建') { steps { script{ runWrapper.runSteps('编译构建') } } } stage('部署') { steps { script{ runWrapper.runSteps('部署') } } } }} 具体构建时执行的步骤是在项目仓库根目录下的jenkins-project.json文件中定义: { "初始化": { "执行脚本": { "Type": "COMMAND_STDOUT", "Script": { "脚本-1": "echo 初始化脚本-1", "脚本-2": "echo 初始化脚本-2" } } }, "代码检查": { "执行脚本": { "Type": "COMMAND_STATUS", "Script": { "脚本-1": "echo 代码检查脚本-1", "脚本-2": "echo 代码检查脚本-2" } } }, "单元测试": { "执行脚本": { "Type": "COMMAND_STATUS", "Script": { "脚本-1": "echo 单元测试脚本-1", "脚本-2": "echo 单元测试脚本-2" } } }, "编译构建": { "执行脚本": { "Type": "COMMAND_STATUS", "Script": { "脚本-1": "echo 编译构建脚本-1", "脚本-2": "echo 编译构建脚本-2" } } }, "部署": { "执行脚本": { "Type": "COMMAND_STATUS_FOR", "For": "1,2,3", "ScriptTemplate": "echo 编译构建脚本-${loop-command-for}" } }} 定义构建具体步骤的json配置文件中,节点名字大多数情况下可以随意定义(除GlobalVariable节点和RuntimeVariable节点),重点是第一层级的节点代表要执行构建步骤的步骤集合(Steps),上述示例中第一层节点对应着Jenkinsfile文件中定义的stage中的steps,使用runWrapper.runSteps()方法执行其中的具体构建步骤(当然在Jenkinsfile中一个steps括号内执行多次runSteps()也是可以的)。 在Steps中可以定义一个或多个具体的构建步骤(Step)节点,每一个Step节点内需要有一个Type节点来声明执行步骤的操作类型,共享类库通过识别Type确定执行方式,上述出现的三个Type是:
其实任何类型的构建步骤节点都可以包含Script子节点,并且Script子节点中可以包含多条命令。 查看构建日志: 开始执行[/root/.jenkins/workspace/Test-Jenkins-Json-Build/jenkins-project.json]的[初始化]开始执行[初始化]的[执行脚本]开始执行[脚本-1]的[echo 初始化脚本-1]命令bash: no job control in this shell执行完成[初始化脚本-1]开始执行[脚本-2]的[echo 初始化脚本-2]命令bash: no job control in this shell执行完成[初始化脚本-2]执行[执行脚本]完成执行[初始化]完成开始执行[/root/.jenkins/workspace/Test-Jenkins-Json-Build/jenkins-project.json]的[代码检查]开始执行[代码检查]的[执行脚本]开始执行[脚本-1]的[echo 代码检查脚本-1]命令bash: no job control in this shell代码检查脚本-1执行完成[0]开始执行[脚本-2]的[echo 代码检查脚本-2]命令bash: no job control in this shell代码检查脚本-2执行完成[0]执行[执行脚本]完成执行[代码检查]完成开始执行[/root/.jenkins/workspace/Test-Jenkins-Json-Build/jenkins-project.json]的[单元测试][Pipeline] echo开始执行[单元测试]的[执行脚本]开始执行[脚本-1]的[echo 单元测试脚本-1]命令bash: no job control in this shell单元测试脚本-1执行完成[0]开始执行[脚本-2]的[echo 单元测试脚本-2]命令bash: no job control in this shell单元测试脚本-2执行完成[0]执行[执行脚本]完成执行[单元测试]完成开始执行[/root/.jenkins/workspace/Test-Jenkins-Json-Build/jenkins-project.json]的[编译构建]开始执行[编译构建]的[执行脚本]开始执行[脚本-1]的[echo 编译构建脚本-1]命令bash: no job control in this shell编译构建脚本-1执行完成[0]开始执行[脚本-2]的[echo 编译构建脚本-2]命令bash: no job control in this shell编译构建脚本-2执行完成[0]执行[执行脚本]完成执行[编译构建]完成开始执行[/root/.jenkins/workspace/Test-Jenkins-Json-Build/jenkins-project.json]的[部署]开始执行[部署]的[执行脚本]开始执行[For-1]的[echo 编译构建脚本-1]命令bash: no job control in this shell编译构建脚本-1执行完成[0]开始执行[For-2]的[echo 编译构建脚本-2]命令bash: no job control in this shell编译构建脚本-2执行完成[0]开始执行[For-3]的[echo 编译构建脚本-3]命令bash: no job control in this shell编译构建脚本-3执行完成[0]执行[执行脚本]完成执行[部署]完成Finished: SUCCESS 从日志中可以看出构建步骤的执行是按照jenkins-project.json的节点定义进行的,所以同一个Jenkinsfile脚本会根据每个项目内不同的jenkins-project.json文件执行不同的构建内容。 (PS:因为在执行脚本前都添加了#!/bin/bash -il命令,目的是加载/etc/profile定义的环境变量,所以每个命令脚本执行时都会有一个bash: no job control in this shell提示,虽然不影响执行但这个问题如果有解决方案请告诉我) json中的变量为了在json中方便配置构建步骤节点,本项目允许在json文档中定义变量并使用变量简化配置内容(因为实际工作中会有大量重复的配置信息)。变量的作用域概念与编写程序一致:就近原则,GlobalVariable节点定义的变量作用域是整个文档,但在每个节点内可以用Variable节点定义多个局部变量,如果Variable变量名称和GlobalVariable定义的变量名称相同,局部变量则会覆盖GlobalVariable定义的全局变量(就近原则),并且Variable定义的局部变量离开定义的节点后则无效,另外定义变量和使用变量有先后顺序,如果在使用之前文档没有定义该变量则变量无效,在定义变量之后使用变量才是正确的,定义变量和引用变量示例: //定义变量//在GlobalVariable节点或Variable节点内"变量名": "变量值"//使用变量//在定义变量之后任何节点的内容中都可以引用变量"节点名称": "${变量名称}" 在文档中可使用Jenkins的env全局变量,比如BUILD_NUMBER、JOB_NAME、JENKINS_URL等,也可以在运行时使用json配置文件中的RuntimeVariable节点定义自己的env变量,还可以直接用GlobalVariable和Variable节点直接在文档中定义全局和局部变量,上述变量加载的顺序是:文档首先加载Jenkins的env全局变量(含构建参数变量)、然后是RuntimeVariable节点定义的变量、然后是GlobalVariable节点定义的变量、最后是Variable节点定义的变量,按照上述变量加载顺序,变量加载后就能使用,其中Jenkins的env全局变量会隐式的加载到文档的全局变量中不用声明即可使用,以下是定义变量和使用变量的示例(示例文件): Jenkinsfile文件内容: @Library('shared-library') _pipeline { parameters { //定义构建参数 choice choices: ['部署全部'], description: '请选择部署方式', name: 'deploy-choice' } agent any stages { stage('输出变量值') { steps { script{ runWrapper.loadJSON('/jenkins-project.json') runWrapper.runSteps('测试变量') } } } }} jenkins-project.json文件内容: { "RuntimeVariable": { //Deploy_Choice是构建参数 "JENKINS_PARAMS_DEPLOY": "echo ${deploy-choice}", //BUILD_URL是Jenkins的env全局变量 "JENKINS_BUILD_URL": "echo ${BUILD_URL}", //会执行pwd命令获取当前目录 "SCRIPT_PWD": "pwd", //执行脚本获取git commit的信息 "SCM_CHANGE_TITLE": "git --no-pager show -s --format=\"%s\" -n 1" }, "GlobalVariable": { //使用RuntimeVariable的JENKINS_PARAMS_DEPLOY变量值 "GV_PARAMS_DEPLOY": "${JENKINS_PARAMS_DEPLOY}", //使用RuntimeVariable的JENKINS_BUILD_URL变量值 "GV_BUILD_URL": "${JENKINS_BUILD_URL}", //定义一个全局的健值对变量 "GV_VAR": "gv_var_value", //定义一个全局的健值对变量 "TEMP_VAR": "temp_var_value" }, "测试变量": { "输出RuntimeVariable变量脚本": { "Type": "COMMAND_STATUS", "Script": { "输出JENKINS_PARAMS_DEPLOY": "echo ${JENKINS_PARAMS_DEPLOY}", "输出JENKINS_BUILD_URL": "echo ${JENKINS_BUILD_URL}", "输出SCRIPT_PWD": "echo ${SCRIPT_PWD}", "输出SCM_CHANGE_TITLE": "echo ${SCM_CHANGE_TITLE}" } }, "输出GlobalVariable变量脚本": { "Type": "COMMAND_STATUS", "Variable": { //覆盖GlobalVariable的TEMP_VAR变量值 "TEMP_VAR": "variable_value", //定义一个局部变量 "Local_Variable": "Local_Variable" }, "Script": { "输出GV_PARAMS_DEPLOY": "echo ${GV_PARAMS_DEPLOY}", "输出GV_BUILD_URL": "echo ${GV_BUILD_URL}", "输出GV_VAR": "echo ${GV_VAR}", "输出TEMP_VAR": "echo ${TEMP_VAR}", "输出Local_Variable": "echo ${Local_Variable}" } } }} 构建日志: 开始执行[测试变量]的[输出RuntimeVariable变量脚本]开始执行[输出JENKINS_PARAMS_DEPLOY]的[echo 部署全部]命令bash: no job control in this shell部署全部执行完成[0]开始执行[输出JENKINS_BUILD_URL]的[echo http://192.168.0.15:8081/job/Test-Jenkins-Json-Build/11/]命令bash: no job control in this shellhttp://192.168.0.15:8081/job/Test-Jenkins-Json-Build/11/执行完成[0]开始执行[输出SCRIPT_PWD]的[echo /root/.jenkins/workspace/Test-Jenkins-Json-Build]命令bash: no job control in this shell/root/.jenkins/workspace/Test-Jenkins-Json-Build执行完成[0]开始执行[输出SCM_CHANGE_TITLE]的[echo test]命令bash: no job control in this shelltest执行完成[0]执行[输出RuntimeVariable变量脚本]完成开始执行[测试变量]的[输出GlobalVariable变量脚本]开始执行[输出GV_PARAMS_DEPLOY]的[echo 部署全部]命令bash: no job control in this shell部署全部执行完成[0]开始执行[输出GV_BUILD_URL]的[echo http://192.168.0.15:8081/job/Test-Jenkins-Json-Build/11/]命令bash: no job control in this shellhttp://192.168.0.15:8081/job/Test-Jenkins-Json-Build/11/执行完成[0]开始执行[输出GV_VAR]的[echo gv_var_value]命令bash: no job control in this shellgv_var_value执行完成[0]开始执行[输出TEMP_VAR]的[echo variable_value]命令bash: no job control in this shellvariable_value执行完成[0]开始执行[输出Local_Variable]的[echo Local_Variable]命令bash: no job control in this shellLocal_Variable执行完成[0]执行[输出GlobalVariable变量脚本]完成执行[测试变量]完成Finished: SUCCESS JENKINS_PARAMS_DEPLOY变量值在执行第二次构建时才能获取到,因为添加构建参数的脚本在Jenkinsfile中,第一次执行时实际上构建任务还没有该构建参数,另外,在RuntimeVariable定义变量是不能和GlobalVariable一样直接用简单的健值对方式赋值,因为在RuntimeVariable定义的变量都需要通过HTTP、读取文件、执行命令脚本这三种方式其中的一种方式获得变量值,所以需要用echo命令来进行赋值。 如果在RuntimeVariable节点中定义的是通过HTTP或读取文件的方式获得一个Json文档,那么可以在URL或文件路径后面写@path[\节点名称\节点名称]来设置节点检索路径获得节点的值后赋值给变量,比如: 在RuntimeVariable节点中通过读取文件给变量赋值: "JAVA_VERSION_SCRIPT": "./src/main/jenkins/com/bluersw/jenkins/libraries/json/java-build.json@path[\\初始化\\检查Java环境\\Script\\输出Java版本]" JAVA_VERSION_SCRIPT变量的值是“java -version 2>&1” 同样在RuntimeVariable节点内也可以通过HTTP协议给变量赋值: "HTTP_JAVA_VERSION_SCRIPT": "https://raw.githubusercontent.com/sunweisheng/jenkins-json-build/master/unit-tests/src/main/jenkins/com/bluersw/jenkins/libraries/json/java-build.json@path[\\初始化\\检查Java环境\\Script\\输出Java版本]" HTTP_JAVA_VERSION_SCRIPT变量的值也是“java -version 2>&1” Json文档中隐式声明(不用声明直接使用)的变量有: BUILD_DISPLAY_NAME:#28BUILD_ID:28BUILD_NUMBER:28BUILD_TAG:jenkins-Test-Jenkins-Json-Build-28BUILD_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/28/CLASSPATH:deploy-choice:模拟部署脚本-1HUDSON_HOME:/root/.jenkinsHUDSON_SERVER_COOKIE:04f54204dabc2b46HUDSON_URL:http://ops.gydev.cn:8080/JENKINS_HOME:/root/.jenkinsJENKINS_SERVER_COOKIE:04f54204dabc2b46JENKINS_URL:http://ops.gydev.cn:8080/JOB_BASE_NAME:Test-Jenkins-Json-BuildJOB_DISPLAY_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/display/redirectJOB_NAME:Test-Jenkins-Json-BuildJOB_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/library.shared-library.version:V2RUN_ARTIFACTS_DISPLAY_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/28/display/redirect?page=artifactsRUN_CHANGES_DISPLAY_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/28/display/redirect?page=changesRUN_DISPLAY_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/28/display/redirectRUN_TESTS_DISPLAY_URL:http://ops.gydev.cn:8080/job/Test-Jenkins-Json-Build/28/display/redirect?page=testsREAL_USER_NAME:(执行构建账号的UserName)WORKSPACE:/root/.jenkins/workspace/Test-Jenkins-Json-BuildPROJECT_PATH:/root/.jenkins/workspace/Test-Jenkins-Json-Build/PROJECT_DIR: 特别说明:
PROJECT_PATH变量经常会用到,在执行命令脚本之前经常要加上cd ${PROJECT_PATH};来定位命令脚本执行时的路径,上面这些隐式声明的变量在文档任何地方都可以直接使用。 统一的构建脚本可以编写一个或几个统一的构建脚本给所有项目使用,这样可以方便开发人员使用和维护,比如如下典型的构建脚本(Jenkinsfile): @Library('shared-library') _pipeline { agent { label params['agent-name'] } parameters { //定义构建参数 choice choices: ['-'], description: '请选择部署方式', name: 'deploy-choice' agentParameter name:'agent-name' checkboxParameter name:'project-list', format:'YAML', uri:'https://raw.githubusercontent.com/sunweisheng/jenkins-json-build/master/example/microservice-build/project-list.yaml' } stages { stage('初始化') { steps { script{ runWrapper.loadJSON(params['project-list']) runWrapper.runSteps('初始化') } } } stage('单元测试') { steps { script{ runWrapper.runSteps('单元测试') } } } stage('代码检查') { steps { script{ runWrapper.runSteps('代码检查') } } } stage('编译构建') { steps { script{ runWrapper.runSteps('编译构建') } } } stage('部署') { steps { script{ runWrapper.runStepForEnv('部署','deploy-choice') } } } }} 上述构建过程分为初始化(一般初始化内含加载构建配置文件、检查构建环境、构建参数的值绑定等内容)、单元测试、代码检查(代码规范)、编译、部署共5个大步骤,适合大多数项目,其中有两个配合使用的Jenkins插件: Agent Server Parameter Plugin插件用于选择构建服务器(Jenkins Agent Node),Custom Checkbox Parameter Plugin用于选择仓库根目录下的子项目实现选择性的构建子项目(如果没有子项目可以不使用此插件),一般情况下还会使用Git Parameter插件一起使用,Git Parameter插件用于选择分支进行源码获取。 定义一个下拉菜单方式的构建参数: //选择某个部署步骤而不是执行所有的部署步骤choice choices: ['-'], description: '请选择部署方式', name: 'deploy-choice' 绑定下拉菜单方式构建参数的值: "绑定构建参数": { "Type": "BUILD_PARAMETER_DROP_DOWN_MENU", "StepsName": "部署", "ParamName": "deploy-choice" } 根据选择的值执行对应名称的构建步骤: runWrapper.runStepForEnv('部署','deploy-choice') runWrapper.runStepForEnv()方法是根据某个全局变量的值来执行Steps中对应名称的构建步骤,在Jenkinsfile中定义了一个下拉菜单用于选择部署方式,在Json配置文件中会配置为其绑定一个Steps内的步骤列表,这样配合runStepForEnv()方法就能达到选择步骤执行的目的。 后面介绍的示例项目都是用统一的Jenkinsfile构建脚本执行,但项目内的json构建配置文件的内容不同,所以构建的内容也不相同。 构建Java项目需要安装的软件构建服务器上需要安装Java、Maven和Sonar-Scanner。 构建Java项目依赖的插件
Java构建的配置文件内容{ "初始化": { "检查Java环境": { "Type": "COMMAND_STDOUT", "Success-IndexOf": "java version \"1.8.0_211\"", "Script": { "输出Java版本": "java -version 2>&1" } }, "检查Maven环境": { "Type": "COMMAND_STDOUT", "Success-IndexOf": "Apache Maven 3.6.3", "Script": { "输出Maven版本": "mvn -v" } }, "检查SonarScanner环境": { "Type": "COMMAND_STDOUT", "Success-IndexOf": "SonarScanner 4.4.0.2170", "Script": { "输出SonarScanner版本": "sonar-scanner -v" } }, "绑定构建参数": { "Type": "BUILD_PARAMETER_DROP_DOWN_MENU", "StepsName": "部署", "ParamName": "deploy-choice" } }, "单元测试": { "执行Maven单元测试脚本": { "Type": "COMMAND_STATUS", "Script": {
|
请发表评论