Jenkins是一款开源CI$CD软件,用于自动化各种任务,包括构建、测试和部署软件
优点:
持续的软件版本发布、测试项目
监控外部调用执行的工作
对于移动端开发来说,使用Jenkins持续化集成,可以帮助开发人员缩短开发周期,开发人员只需要关注开发任务,像给产品、测试人员打包时,这些任务就可以交给Jenkins来做,测试人员可只需要扫描一下二维码安装即可。
先来一张效果图
安装
这里我们通过homebrew安装,如果未安装Homebrew,先安装Homebrew,详见Homebrew安装和使用
Homebrew安装完成后,执行以下命令安装Jenkins
brew install jenkins
安装完成后,执行war包
java -jar /usr/local/Cellar/jenkins/2.183/libexec/jenkins.war
这里Jenkins版本号可根据自己的Jenkins版本进行更换
另附启动和关闭Jenkins命令:
启动
jenkins -h
关闭
control + c 快捷键关闭
启动后,先不要急着打开Jenkins的web容器,先去/Library/LaunchDaemons目录下新建一个org.jenkins-ci.plist文件,文件内容如下(可直接拷贝修改JENKINS_HOME值为你自己的路径)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StandardOutPath</key>
<string>/var/log/jenkins/jenkins.log</string>
<key>StandardErrorPath</key>
<string>/var/log/jenkins/jenkins.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>JENKINS_HOME</key>
<string>/Users/aladin/Documents/Jenkins/Home</string>
</dict>
<key>GroupName</key>
<string>daemon</string>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>org.jenkins-ci</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Library/Application Support/Jenkins/jenkins-runner.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>jenkins</string>
<key>SessionCreate</key>
<true/>
</dict>
</plist>
启动Jenkins
为避免权限问题,先执行下面的命令:
sudo chown root /usr/local/Cellar/jenkins/2.122/homebrew.mxcl.jenkins.plist
启动完成后,打开浏览器,输入http://localhost:8080会出现如下页面:
稍等几分钟,会出现如下页面
进入到Jenkins提示的目录(我这里是/Users/Shared/Jenkins/Home/secrets/initialAdminPassword)获取管理员密码,在该路径中,非Jenkins用户secrets目录和initialAdminPassword文件时没有读写权限的,将该权限改成只读或读和写:
获取到密码后,记得备份下,后期可能会用到
配置
1、安装插件
可以按照推荐的插件安装,也可以自己选择
这里我们选择安装推荐的插件
安装完成后,会提示我们创建用户
创建完成后,会提示我们配置Jenkins URL,这个可以根据自身情况进行修改,这里我们先用默认设置http://localhost:8080/
Jenkins插件安装
因为公司项目托管在gitlab上,所以这里需要安装gitlab插件,同时安装Git Parameter插件和Build Name and Description Setter(用于参数化构建)
安装Dynamic Parameter插件
由于Dynamic Parameter插件有漏洞,在Jenkins中搜索不到,这里给出下载地址,经测试目前只有0.1.1版本能用,该插件下载完成后需要在插件管理-高级-上传插件中进行安装
插件安装完成后,我们就来进行基本环境的配置
2、环境配置
2.1系统配置:
增加环境变量
进入Manage Jenkins -> Configure System -> 全局属性,勾选Environment variables,增加一对键值
其中PATH的值为本机的环境变量,可以在终端执行以下命令查看,为一堆路径:
echo $PATH
Android sdk配置:
SDK配置中的键必须是ANDROID_HOME,值为你本机的Android SDK目录,这里要注意SDK目录的权限问题,没有权限的话,可能会导致后期构建的时候提示找不到SDK路径。
JDK、Git、Gradle配置:
2.2Gradle配置
接下来看下build.gradle中的部分配置
apply plugin: 'com.android.application'
def fileArray = []
def getBuildTime() {
return new Date().format("yyyy-MM-dd-HH-mm")
}
//是否Jenkins打包
def isJenkins() {
return "true".equals(IS_JENKINS)
}
//是否是Google渠道
def isGpChannel() {
return "gp".equals(JENKINS_CHANNEL)
}
//获取Channel
def getJenkinsChannel() {
// def channels = System.getenv("JENKINS_CHANNEL")
def channels = JENKINS_CHANNEL
println("多渠道:" + channels)
// String channels = "yingyongbao"
channels.toString().tokenize(',').each { channelItem ->
android.productFlavors.create(channelItem, {
manifestPlaceholders = [
APP_NAME : APP_NAME,
CHANNEL_VALUE: channelItem,
API_DOMAIN : API_DOMAIN
]
println("当前渠道:" + channelItem)
})
// android.sourceSets.main.manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
android {
compileSdkVersion 28
buildToolsVersion "29.0.0"
defaultConfig {
String packageName = "com.ywd.jenkinsbuildtest"
if (isJenkins()) {
packageName = PACKAGE_NAME
}
applicationId packageName
minSdkVersion 19
targetSdkVersion 28
versionCode 100
versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//确保所有的flavors都属于同一维度
flavorDimensions "default"
// sourceSets.main {
// jni.srcDirs = []
// //LOCAL_LDFLAGS += -fuse-ld=bfd
// //jni.srcDirs 'src/main/jni'
// jniLibs.srcDir 'src/main/libs'
// }
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
preview {
minifyEnabled false
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled true
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
getJenkinsChannel()
// productFlavors {
// def channel = "gp"
// println("productFlavors_isJenkins":isJenkins())
// if(isJenkins()){
// channel = CHANNEL_VALUE
// }
// println(channel)
// app {
// manifestPlaceholders = [CHANNEL_VALUE: channel]
// }
// }
applicationVariants.all { variant ->
variant.outputs.all { output ->
def appVersion = variant.versionName //版本号
def buildType = "" //构建类型
def buildTime = getBuildTime() //构建时间
println("IS_JENKINS_${IS_JENKINS}")
if ("true".equals(IS_JENKINS)) {
appVersion = APP_VERSION
buildTime = BUILD_TIME
}
//构建类型
if ("debug".equals(variant.buildType.name)) {
buildType = "Debug"
} else if ("preview".equals(variant.buildType.name)) {
buildType = "Preview"
} else {
buildType = "Release"
}
def fileName = "${appVersion}_${variant.productFlavors[0].name}_${buildTime}_${buildType}.apk"
def outFile = output.outputFile
if (outFile != null && outFile.name.endsWith('.apk')) {
outputFileName = fileName
}
fileArray.add(outFile.parentFile.absolutePath + File.separator + fileName)
}
}
//根据不同场景配置不同的AndroidManifest.xml文件
sourceSets {
println("==============sourceSets==============")
main {
if (isGpChannel()) {
manifest.srcFile 'src/main/gp/AndroidManifest.xml'
println("使用Google配置")
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
println("使用默认配置")
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
afterEvaluate {
//只有Jenkins打包才复制,此处复制到文件下载路径
if (isJenkins()) {
assembleDebug.doLast {
forEachFile(fileArray)
}
assemblePreview.doLast {
forEachFile(fileArray)
}
assembleRelease.doLast {
forEachFile(fileArray)
}
}
}
def forEachFile(fileArray) {
fileArray.forEach { file ->
renameAndMoveoutApk(file)
}
}
def renameAndMoveoutApk(orignalFile) {
//此处路径根据实际情况设置
def intoFile = rootDir.parentFile.parentFile.parentFile.parentFile.getAbsolutePath() + File.separator + "Shared/apache-tomcat-9.0.21/webapps/Jenkins_apk"
copy {
println("开始复制:目标路径:${intoFile}")
from orignalFile
into intoFile
// rename("${android.defaultConfig.versionName}_${android.defaultConfig.versionCode}_","")
}
}
gradle.properties
IS_JENKINS = false
BUILD_TIME = 2019-09-04
APP_NAME = JenkinsBuildTest
APP_VERSION = 1.0.0
PACKAGE_NAME = com.ywd.jenkinsbuildtest1
# 渠道
JENKINS_CHANNEL = app
# 接口域名
API_DOMAIN = ""
2.3Jenkins项目配置
新建项目
填写项目名称,这里我们选择构建一个自由风格的软件项目
创建好后,进入项目配置
选择参数化构建,添加参数GitParameter
这里变量名随意,参数类型选择Branch or Tag
添加参数,选择Choice Parameter,这里参数名为IS_JENKINS,注意这里参数名要和gradle.properties中定义的相同
创建参数BUILD_TYPE,这里名字可以随意,参数根据自己项目中定义
添加Dynamic Parameter
创建参数BUILD_TIME,注意这里参数名要和gradle.properties中定义的相同
并且Dynamic Parameter使用的是Groovy Script
创建
源码管理
这里我们使用的是Git
输入仓库地址后,点击添加,添加认证
创建完成后,选择刚刚创建的用户凭据,并填写上面参数化构建填好的分支变量名,注意变量名前要加$
选择构建插件
Android使用的是Gradle构建,这里我们选择之前配置好的Gradle版本,并输入以下命令
clean assemble${BUILD_TYPE} --stacktrace
然后勾选Pass all job parameters as Project properties,旧版本是勾选Pass job parameters as Gradle properties
配置完成后,点击保存,回到项目首页
可以看到,原先的立即构建已经变成了Build with Parameters
配置完成后,点击开始构建,这里我们可以查看控制台输出
在控制台我们可以看到和Android Studio打包同样的输出结果,最后显示构建成功。
构建完成后,我们可以在项目目录找到打好的包
构建名称
原本的构建名称只是一个编号,对于使用人员来讲,没有辨识度,我们可以在项目的构建环境中进行配置,更改名称,具体操作如下,在构建环境中勾选Set Build Name,并填入上文配置的参数名称
保存后,我们再次构建查看下结果,构建名称已经改变了
经过如上配置,我们的Jenkins打包就可以正常工作了,但是构建完成的包,测试人员该怎么安装呢,不能每次打完包还要我们去项目目录下找到发给他们吧,这是不可能的,让测试区工作区自己找?可不太可能。。。接下来我们卡一下如何将打完的包生成二维码并展示
3、生成二维码并展示
3.1 Tomcat安装及配置
安装并配置Tomcat,详见Mac安装Tomcat
修改配置文件conf/web.xml
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
把原来的false改为true,此时在webapps下新建个目录,如download,就可以通过浏览器访问里面的内容
3.2 Python安装及配置
安装Python和pip详见Mac安装Python和pip
安装Pillow
输入命令sudo pip install Pillow,出现如下提示说明已经安装完成
3.3 qrcode安装及配置
输入以下命令
pip3 install myqr
以上配置完成后,打开Jenkins,进入Manage Jenkins -> 全局属性,然后新增属性,添加Python全局变量
3.4 生成二维码
进入项目 -> 配置 -> 构建,增加构建步骤
填写如下命令
myqr http://172.20.41.235:8888/Jenkins_apk/${APP_VERSION}_${JENKINS_CHANNEL}_${BUILD_TIME}_${BUILD_TYPE}.apk -n ${APP_VERSION}_${JENKINS_CHANNEL}_${BUILD_TIME}_${BUILD_TYPE}.png -v 1 -l L -d /Users/Shared/apache-tomcat-9.0.21/webapps/Jenkins_apk
其中路径和${}里面的参数根据自己的实际情况进行配置
关于qrcode的详细使用,详见Github
3.5 展示二维码
通过myqr命令会在Tomcat下载目录生成一张二维码图片,接下来我们要把二维码图片显示在Jenkins上:
安装插件description setter plugin,安装好后,进入项目->配置->构建后操作,增加构建后操作步骤,选择Set build description
我这里已经设置过了build description,所以是灰色的
选择完成后,描述可以添加HTML标签,所以我们可以将<img src='' />标签加入到描述中,不过这里有个问题,加了img标签后,Jenkins并不会显示二维码图片,这是因为Jenkins出于安全考虑,所有描述信息的Markup Formatter默认是采用Plain text模式,这种模式不会对描述信息中的HTML编码进行解析。
我们可以在Manage Jenkins -> Configure Global Security,将Markup Formatter的设置改为Safe HTML即可。
Description中的描述
<img src='http://172.20.41.235:8888/Jenkins_apk/${APP_VERSION}_${JENKINS_CHANNEL}_${BUILD_TIME}_${BUILD_TYPE}.png' height="200" width="200" /><br><a href="http://172.20.41.235:8888/Jenkins_apk/${APP_VERSION}_${JENKINS_CHANNEL}_${BUILD_TIME}_${BUILD_TYPE}.apk">下载连接</a>
其中地址和参数可根据自身实际情况进行配置。
4、常见问题
1、
Caused by: java.lang.RuntimeException: The SDK directory '/Users/aladin/Library/Android/sdk' does not exist.
这个路径是SDK默认的路径,刚开始是以为jenkins没有把local.properties文件拉下来,结果这个文件拉下来之后,还是报这个错,后来估计是权限问题,然后将everyone只读权限放在Library目录下就没问题了
参考文章:
Android-解放双手告别测试-使用Jenkins自动化打包
Android使用Jenkins持续集成
Jenkins本地搭建遇到的问题 for Mac