Mac 下的 Jenkins 安装
安装 Jenkins 之前,⾸先检查⼀下 Mac 上⾯的是否安装了下⾯的软件
- Java (必须是 8 ~ 11 的版本)
可以通过终端命令 Java -version 来查看本地的 Java 版本 - Android SDK (打包apk必备)
- Android Gradle (打包apk必备)
安装了 Android Studio 之后,在终端中的根⽬录输⼊ open .gradle 即可打开 gradle 的⽂件
夹 -
Git
在解决上⾯的环境问题之后,就可以去官⽹下载 Jenkins 了
image.png
可以选择下载 左边的⻓期版本 或 右边的周更版本,哪个都可以使⽤,这⾥笔者是使⽤了周更版本
下载完成并完成安装后,会得到⼀个 许可.pdf 和 ⼀个 jenkins.war 的运⾏包
image.png
这时候需要在终端切换到 jenkins.war 所在路径,然后输⼊:
java -jar jenkins.war --httpPort=8080
⼀切顺利的话,就会看到下⾯的终端输出如下打印,这是在启动 Jenkins :
image.png
这时候就可以打开浏览器,输⼊: http://localhost:8080/ ,进⼊ Jenkins 的控制台了,进⾏我们
需要的配置了
Jenkins 的初始化
如果是第⼀次启动 Jenkins,那么它会要求你输⼊⼀个管理员密码:
按照提示找到该⽂件,然后输⼊密码,按下 next ,就会看到下⾯的⻚⾯:
这⾥建议安装推荐的插件,等以后玩得熟悉了可以⾃⼰来定制,不过某些插件的下载可能需要梯⼦,否
则下载速度会⽐较慢
等全部插件都下载完成后,就可以看到下⾯的⽤户名设置⻚⾯了:
登陆⽤户创建完成后,就是设置进⼊ Jenkins 控制台的 url 了,这⾥笔者沿⽤默认的:
现在⼀切都准备就绪,可以开始运⾏ Jenkins 了
Jenkins 打包 apk
这时候 Jenkins 已经运⾏起来了,不过在构建打包任务之前,需要先配置好⼀些环境变量:
配置 SDK 路径
到 系统设置 的全局属性⾥⾯添加 ANDROID_HOME ,也就是本地的 SDK 的路径
然后点击左下⽅的 应⽤ 并 保存
注意:这⾥必须先点击应⽤再去进⾏保存,如果不点应⽤的话,保存可能会⽆效
配置 JDK 、Git 和 Gradle 的路径
SDK 路径设置好后,就可以去配置 JDK 、Git 和 Gradle 的路径了:
需要注意的是,如果不是版本不兼容的情况,这⾥建议不要使⽤ ⾃动安装 的功能,尤其是 JDK 和
Gradle,现在 JDK 的下载安装需要⼀个 Oracle 账号,⽽ Gradle 没有梯⼦的话,下载也是⾮常的慢
因此直接去设置它们的本地依赖路径(最好就是项⽬当前使⽤的版本),然后点击应⽤并保存即可
PS:笔者的 Mac 由于 Git 版本较低,⽽且 Jenkins 也没有提示要升级到那个版本,为了避免升级
之后还是⽆法兼容的尴尬,这⾥ Git 选了 ⾃动安装 来进⾏升级
配置项⽬中的gradle脚本
这部分需要在项⽬中配置,⽤ Android Studio 打开项⽬并打开 app 模块⾥⾯的 build.gradle ,这
⾥需要配置打包执⾏的命令(直接沿⽤项⽬中的配置即可):
apply plugin: 'com.android.application'
android {
defaultConfig {
applicationId "xxx.xxxx.xxxxx"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName 1.0
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
//配置打包的密钥数据
signingConfigs {
config {
keyAlias 'key的别名'
keyPassword 'key的密码'
storeFile file('这⾥是打包key的路径')
storePassword 'key的打包密码'
}
}
//配置打包的执⾏命令并设置别名
buildTypes {
release {
//这⾥配置别名为release的打包脚本
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
//配置打包⽤的密钥
signingConfig signingConfigs.config
}
}
}
dependencies {
//......
}
创建打包任务
现在就是最后⼀步了,创建⼀个 Jenkins 的打包任务
给打包任务起⼀个名字,按照下图中去配置即可:
点击确认后就可以看到下⾯的六个配置:
这些配置全部都有作⽤,不过这⾥先针对⼀部分来讲解
源码管理
如果是第⼀次配置的话,需要点击 添加 按钮来添加⽤户的凭证:
如果后期需要修改打包的分⽀,这⾥可以随时修改的
构建
这⾥打包是 Android apk,因此选择 gradle 构建的⽅式:
然后选择已经配置好的 gradle 以及添加打包执⾏的指令:
选中第⼀个 Invoke Gradle ,Gradle Version 就⽤我们之前配置的 gradle 就可以了,⽽需要执⾏的
Task 则就是:
app:clean
app:assembleRelease
然后点击上⾯的 ⾼级 按钮,给下⾯两个选项打勾:
执⾏构建任务
返回⾸⻚,可以在主⻚⾯看到刚才创建的任务,点击 右边的箭头 可以看到⼀系列的菜单,可以在这⾥
选择 ⽴即构建,也可以点击最右边的按钮来执⾏任务
这时候就会看到下⾯的构建过程了:
然后点击上图的⼩箭头,然后打开控制台输出,可以看到打包的编译情况:
不过第⼀次编译时⽐较慢的,因为将项⽬的依赖包都下载到本地服务器上⾯,建议找个梯⼦来加快编译
速度
可以看到它还是下载了不少代码包的,不过只要代码没问题,这⾥⼀般都可以看到 BUILD
SUCCESSFUL ,顺利完成打包
上传 apk 到 服务器
⽬前已经顺利把 apk 打包出来了,但是⽬前打包出来是存放在 Jenkins 的本地服务器上⾯,还是不太
⽅便的,因此最好就是把服务器打包出来的 apk 进⾏上传
这⾥上传 apk 到服务器,可以和公司后台配合搭建⼀个 ftp 服务器 来上传,也可以选择 fir.im 或 蒲公
英 等分发平台,这⾥笔者选择了 fir.im,毕竟⽤过⼤半年的
在登录后 fir.im 官⽹的个⼈资料中有⼀个 API token :
先把这个记下来,然后到 http://blog.fir.im/jenkins/ 下载 Jenkins 的 fir.im的插件,下载完成后就到
系统管理 -> 插件管理->⾼级 ⾥⾯来安装这个本地插件:
选中刚才下载的插件后,上传即可
安装完成后,再⼀次打开我们刚才构建的任务的配置,到 构建后操作 ⾥⾯,这时可以看到新的元素
了:
选中后,填好下⾯配置即可:
然后点击 应⽤并保存 。然后再⼀次构建项⽬,构建成功后我们会在控制台输出看到下⾯的打印:
说明已经上传到 fir.im 了,现在到 fir.im ⾥⾯可以看到下⾯的信息,可以到 fi r.im 的链接查看更新时间来判断是否已经上传成功,这时候,测试⼈员就只需要通过 fir.im 下载链接即可下载最新的测试 apk 了。
当使用蒲公英的话看下文
通知到指定钉钉群
1.设置钉钉群的机器人
2.Jenkins安装一下钉钉相关的插件
安装完成后,就有2种方法来配置钉钉机器人
1.在jenkins配置面板里面配置
这里配置好后,我们的打包都可以受到钉钉机器人的通知了
但是缺点就是,消息格式是统一的,无法进行自定义消息通知(因此一般这里就配置构建失败的通知)
如果要制定 自定义的消息通知,那么就需要使用下面的方法了;
2.构建完成后调用钉钉机器人api
由于 Jenkins 的钉钉插件没有足够的自由化配置
如果需要在构建成功时,输出更多的信息,那么就需要手动配置
在构建成功时去请求钉钉机器人 api,这样子就可以自由配置了:
这里贴一下完整的 Groovy 代码如下(目前iOS项目也是参考这份代码来弄钉钉机器人消息的):
import java.text.SimpleDateFormat
//构建结果
def buildResult = manager. getResult()
//构建用户
def buildUser= manager.getEnvVariable("BUILD_USER")
//项目名称
def jobName= manager.getEnvVariable("JOB_NAME")
//构建结果页面
def buildUrl= "[本地项目url路径](本地项目url路径)"
//GIT分支
def gitBranch= manager.getEnvVariable("GIT_BRANCH")
//构建环境
def buildEnv= manager.getEnvVariable("BUILD_ENV")
//构建类型
def buildType= manager.getEnvVariable("BUILD_TYPE")
//安装密码
def downloadPwd= "1234"
manager.listener.logger.println("项目名称:"+ jobName)
manager.listener.logger.println("构建用户:"+buildUser)
manager.listener.logger.println("构建分支:"+gitBranch)
manager.listener.logger.println("构建环境:"+ buildEnv)
manager.listener.logger.println("构建类型:"+ buildType)
manager.listener.logger.println("构建结果:"+buildResult)
if(buildResult == "SUCCESS"){
//解析蒲公英上传返回数据
//apk下载地址
apkDownloadUrl = "[https://www.pgyer.com/kaC5](https://www.pgyer.com/kaC5)"
//apk二维码
apkQrCode = manager.getEnvVariable("appQRCodeURL")
manager.listener.logger.println("apk下载地址"+apkDownloadUrl)
manager.listener.logger.println("apk二维码地址:"+apkQrCode)
dingding("Jenkins通知","### 【"+jobName+"】构建成功" +
"\n\n构建版本:"+gitBranch+
"\n\n构建环境:"+buildEnv+
"\n\n构建类型:"+buildType+
"\n\n下载地址:\n"+apkDownloadUrl+
"\n\n全历史版本下载链接:"+"http://fritz_xu.gitee.io/apkhistory/"+
"\n\n安装密码:"+downloadPwd+
"\n\n"+
"\n\n构建用户:"+buildUser+
"\n\n构建时间:"+getNowTime()+
"\n\n查看详情:[项目地址]("+buildUrl+")"
)
}else if(buildResult == "ABORTED"){
dingding("Jenkins通知","### 【"+jobName+"】构建被终止" +
"\n\n构建版本:"+gitBranch+
"\n\n构建用户:"+buildUser+
"\n\n构建时间:"+getNowTime()+
"\n\n查看详情:[项目地址]("+buildUrl+")"
)
}else{
dingding("Jenkins通知","### 【"+jobName+"】构建失败" +
"\n\n构建版本:"+gitBranch+
"\n\n构建用户:"+buildUser+
"\n\n构建时间:"+getNowTime()+
"\n\n查看详情:[项目地址]("+buildUrl+")"
)
}
//发送钉钉消息
def dingding(p_title,p_text){
manager.listener.logger.println("--------------------------"+p_title+p_text)
def json= new groovy.json.JsonBuilder()
json {
msgtype "markdown"
markdown {
title p_title
text p_text
}
at {
atMobiles([])
isAtAll false
}
}
manager.listener.logger.println("发送钉钉数据:"+json)
def connection = new URL("[https://oapi.dingtalk.com/robot/send?access_token=514df996d947772a4c53dda6783d3e4c120ca33983fc3792cdb4083ecb43f2b3](https://oapi.dingtalk.com/robot/send?access_token=514df996d947772a4c53dda6783d3e4c120ca33983fc3792cdb4083ecb43f2b3)").openConnection()
connection.setRequestMethod('POST')
connection.doOutput = true
connection.setRequestProperty('Content-Type', 'application/json')
def writer = new OutputStreamWriter(connection.outputStream)
writer.write(json.toString());
writer.flush()
writer.close()
connection.connect()
def respText = connection.content.text
manager.listener.logger.println("钉钉返回结果:"+respText )
}
//获取当前时间
def getNowTime(){
def str = "";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar lastDate = Calendar.getInstance();
str = sdf.format(lastDate.getTime());
return str;
}