Jenkins是一款開源CI$CD軟件芹关,用于自動化各種任務(wù),包括構(gòu)建行瑞、測試和部署軟件
優(yōu)點:
持續(xù)的軟件版本發(fā)布奸腺、測試項目
監(jiān)控外部調(diào)用執(zhí)行的工作
對于移動端開發(fā)來說,使用Jenkins持續(xù)化集成血久,可以幫助開發(fā)人員縮短開發(fā)周期突照,開發(fā)人員只需要關(guān)注開發(fā)任務(wù),像給產(chǎn)品氧吐、測試人員打包時讹蘑,這些任務(wù)就可以交給Jenkins來做,測試人員可只需要掃描一下二維碼安裝即可筑舅。
先來一張效果圖
安裝
這里我們通過homebrew安裝座慰,如果未安裝Homebrew,先安裝Homebrew翠拣,詳見Homebrew安裝和使用
Homebrew安裝完成后版仔,執(zhí)行以下命令安裝Jenkins
brew install jenkins
安裝完成后,執(zhí)行war包
java -jar /usr/local/Cellar/jenkins/2.183/libexec/jenkins.war
這里Jenkins版本號可根據(jù)自己的Jenkins版本進(jìn)行更換
另附啟動和關(guān)閉Jenkins命令:
啟動
jenkins -h
關(guān)閉
control + c 快捷鍵關(guān)閉
啟動后误墓,先不要急著打開Jenkins的web容器蛮粮,先去/Library/LaunchDaemons目錄下新建一個org.jenkins-ci.plist文件,文件內(nèi)容如下(可直接拷貝修改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
為避免權(quán)限問題谜慌,先執(zhí)行下面的命令:
sudo chown root /usr/local/Cellar/jenkins/2.122/homebrew.mxcl.jenkins.plist
啟動完成后然想,打開瀏覽器,輸入http://localhost:8080會出現(xiàn)如下頁面:
稍等幾分鐘欣范,會出現(xiàn)如下頁面
進(jìn)入到Jenkins提示的目錄(我這里是/Users/Shared/Jenkins/Home/secrets/initialAdminPassword)獲取管理員密碼变泄,在該路徑中,非Jenkins用戶secrets目錄和initialAdminPassword文件時沒有讀寫權(quán)限的熙卡,將該權(quán)限改成只讀或讀和寫:
獲取到密碼后杖刷,記得備份下励饵,后期可能會用到
配置
1驳癌、安裝插件
可以按照推薦的插件安裝,也可以自己選擇
這里我們選擇安裝推薦的插件
安裝完成后役听,會提示我們創(chuàng)建用戶
創(chuàng)建完成后颓鲜,會提示我們配置Jenkins URL,這個可以根據(jù)自身情況進(jìn)行修改典予,這里我們先用默認(rèn)設(shè)置http://localhost:8080/
Jenkins插件安裝
因為公司項目托管在gitlab上甜滨,所以這里需要安裝gitlab插件,同時安裝Git Parameter插件和Build Name and Description Setter(用于參數(shù)化構(gòu)建)
安裝Dynamic Parameter插件
由于Dynamic Parameter插件有漏洞瘤袖,在Jenkins中搜索不到衣摩,這里給出下載地址,經(jīng)測試目前只有0.1.1版本能用,該插件下載完成后需要在插件管理-高級-上傳插件中進(jìn)行安裝
插件安裝完成后捂敌,我們就來進(jìn)行基本環(huán)境的配置
2艾扮、環(huán)境配置
2.1系統(tǒng)配置:
增加環(huán)境變量
進(jìn)入Manage Jenkins -> Configure System -> 全局屬性既琴,勾選Environment variables,增加一對鍵值
其中PATH的值為本機(jī)的環(huán)境變量泡嘴,可以在終端執(zhí)行以下命令查看甫恩,為一堆路徑:
echo $PATH
Android sdk配置:
SDK配置中的鍵必須是ANDROID_HOME,值為你本機(jī)的Android SDK目錄酌予,這里要注意SDK目錄的權(quán)限問題磺箕,沒有權(quán)限的話,可能會導(dǎo)致后期構(gòu)建的時候提示找不到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("當(dāng)前渠道:" + 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 = "" //構(gòu)建類型
def buildTime = getBuildTime() //構(gòu)建時間
println("IS_JENKINS_${IS_JENKINS}")
if ("true".equals(IS_JENKINS)) {
appVersion = APP_VERSION
buildTime = BUILD_TIME
}
//構(gòu)建類型
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)
}
}
//根據(jù)不同場景配置不同的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("使用默認(rèn)配置")
}
}
}
}
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打包才復(fù)制建椰,此處復(fù)制到文件下載路徑
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) {
//此處路徑根據(jù)實際情況設(shè)置
def intoFile = rootDir.parentFile.parentFile.parentFile.parentFile.getAbsolutePath() + File.separator + "Shared/apache-tomcat-9.0.21/webapps/Jenkins_apk"
copy {
println("開始復(fù)制:目標(biāo)路徑:${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項目配置
新建項目
填寫項目名稱击困,這里我們選擇構(gòu)建一個自由風(fēng)格的軟件項目
創(chuàng)建好后,進(jìn)入項目配置
選擇參數(shù)化構(gòu)建广凸,添加參數(shù)GitParameter
這里變量名隨意阅茶,參數(shù)類型選擇Branch or Tag
添加參數(shù),選擇Choice Parameter谅海,這里參數(shù)名為IS_JENKINS脸哀,注意這里參數(shù)名要和gradle.properties中定義的相同
創(chuàng)建參數(shù)BUILD_TYPE,這里名字可以隨意扭吁,參數(shù)根據(jù)自己項目中定義
添加Dynamic Parameter
創(chuàng)建參數(shù)BUILD_TIME撞蜂,注意這里參數(shù)名要和gradle.properties中定義的相同
并且Dynamic Parameter使用的是Groovy Script
創(chuàng)建
源碼管理
這里我們使用的是Git
輸入倉庫地址后,點擊添加侥袜,添加認(rèn)證
創(chuàng)建完成后蝌诡,選擇剛剛創(chuàng)建的用戶憑據(jù),并填寫上面參數(shù)化構(gòu)建填好的分支變量名枫吧,注意變量名前要加$
選擇構(gòu)建插件
Android使用的是Gradle構(gòu)建浦旱,這里我們選擇之前配置好的Gradle版本,并輸入以下命令
clean assemble${BUILD_TYPE} --stacktrace
然后勾選Pass all job parameters as Project properties九杂,舊版本是勾選Pass job parameters as Gradle properties
配置完成后颁湖,點擊保存,回到項目首頁
可以看到例隆,原先的立即構(gòu)建已經(jīng)變成了Build with Parameters
配置完成后甥捺,點擊開始構(gòu)建,這里我們可以查看控制臺輸出
在控制臺我們可以看到和Android Studio打包同樣的輸出結(jié)果镀层,最后顯示構(gòu)建成功镰禾。
構(gòu)建完成后,我們可以在項目目錄找到打好的包
構(gòu)建名稱
原本的構(gòu)建名稱只是一個編號,對于使用人員來講吴侦,沒有辨識度谷饿,我們可以在項目的構(gòu)建環(huán)境中進(jìn)行配置,更改名稱妈倔,具體操作如下博投,在構(gòu)建環(huán)境中勾選Set Build Name,并填入上文配置的參數(shù)名稱
保存后盯蝴,我們再次構(gòu)建查看下結(jié)果毅哗,構(gòu)建名稱已經(jīng)改變了
經(jīng)過如上配置,我們的Jenkins打包就可以正常工作了捧挺,但是構(gòu)建完成的包虑绵,測試人員該怎么安裝呢,不能每次打完包還要我們?nèi)ロ椖磕夸浵抡业桨l(fā)給他們吧闽烙,這是不可能的翅睛,讓測試區(qū)工作區(qū)自己找?可不太可能黑竞。捕发。。接下來我們卡一下如何將打完的包生成二維碼并展示
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法挨,就可以通過瀏覽器訪問里面的內(nèi)容
3.2 Python安裝及配置
安裝Python和pip詳見Mac安裝Python和pip
安裝Pillow
輸入命令sudo pip install Pillow,出現(xiàn)如下提示說明已經(jīng)安裝完成
3.3 qrcode安裝及配置
輸入以下命令
pip3 install myqr
以上配置完成后幅聘,打開Jenkins凡纳,進(jìn)入Manage Jenkins -> 全局屬性,然后新增屬性帝蒿,添加Python全局變量
3.4 生成二維碼
進(jìn)入項目 -> 配置 -> 構(gòu)建荐糜,增加構(gòu)建步驟
填寫如下命令
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
其中路徑和${}里面的參數(shù)根據(jù)自己的實際情況進(jìn)行配置
關(guān)于qrcode的詳細(xì)使用,詳見Github
3.5 展示二維碼
通過myqr命令會在Tomcat下載目錄生成一張二維碼圖片陵叽,接下來我們要把二維碼圖片顯示在Jenkins上:
安裝插件description setter plugin狞尔,安裝好后丛版,進(jìn)入項目->配置->構(gòu)建后操作巩掺,增加構(gòu)建后操作步驟,選擇Set build description
我這里已經(jīng)設(shè)置過了build description页畦,所以是灰色的
選擇完成后胖替,描述可以添加HTML標(biāo)簽,所以我們可以將<img src='' />標(biāo)簽加入到描述中,不過這里有個問題独令,加了img標(biāo)簽后端朵,Jenkins并不會顯示二維碼圖片,這是因為Jenkins出于安全考慮燃箭,所有描述信息的Markup Formatter默認(rèn)是采用Plain text模式冲呢,這種模式不會對描述信息中的HTML編碼進(jìn)行解析。
我們可以在Manage Jenkins -> Configure Global Security招狸,將Markup Formatter的設(shè)置改為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 >下載連接</a>
其中地址和參數(shù)可根據(jù)自身實際情況進(jìn)行配置。
4裙戏、常見問題
1乘凸、
Caused by: java.lang.RuntimeException: The SDK directory '/Users/aladin/Library/Android/sdk' does not exist.
這個路徑是SDK默認(rèn)的路徑,剛開始是以為jenkins沒有把local.properties文件拉下來累榜,結(jié)果這個文件拉下來之后营勤,還是報這個錯,后來估計是權(quán)限問題壹罚,然后將everyone只讀權(quán)限放在Library目錄下就沒問題了
參考文章:
Android-解放雙手告別測試-使用Jenkins自動化打包
Android使用Jenkins持續(xù)集成
Jenkins本地搭建遇到的問題 for Mac