Jenkins配置-Android自動化打包-Mac版

Jenkins是一款開源CI$CD軟件,用于自動化各種任務(wù),包括構(gòu)建、測試和部署軟件
優(yōu)點(diǎn):
持續(xù)的軟件版本發(fā)布、測試項(xiàng)目
監(jiān)控外部調(diào)用執(zhí)行的工作
對于移動端開發(fā)來說,使用Jenkins持續(xù)化集成,可以幫助開發(fā)人員縮短開發(fā)周期,開發(fā)人員只需要關(guān)注開發(fā)任務(wù),像給產(chǎn)品、測試人員打包時,這些任務(wù)就可以交給Jenkins來做,測試人員可只需要掃描一下二維碼安裝即可。
先來一張效果圖


效果圖.jpg

安裝

這里我們通過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)如下頁面:

jenkins_install_finish.jpg

稍等幾分鐘,會出現(xiàn)如下頁面
unlock_jenkins.jpg

進(jìn)入到Jenkins提示的目錄(我這里是/Users/Shared/Jenkins/Home/secrets/initialAdminPassword)獲取管理員密碼,在該路徑中,非Jenkins用戶secrets目錄和initialAdminPassword文件時沒有讀寫權(quán)限的,將該權(quán)限改成只讀或讀和寫:
secret.jpg

edit.jpg

獲取到密碼后,記得備份下,后期可能會用到

配置

1、安裝插件

可以按照推薦的插件安裝,也可以自己選擇

20190701100537.jpg

這里我們選擇安裝推薦的插件
install_plugins.jpg

安裝完成后,會提示我們創(chuàng)建用戶
創(chuàng)建用戶.jpg

創(chuàng)建完成后,會提示我們配置Jenkins URL,這個可以根據(jù)自身情況進(jìn)行修改,這里我們先用默認(rèn)設(shè)置http://localhost:8080/
Jenkins插件安裝
因?yàn)楣卷?xiàng)目托管在gitlab上,所以這里需要安裝gitlab插件,同時安裝Git Parameter插件和Build Name and Description Setter(用于參數(shù)化構(gòu)建)
manage plugins.jpg

plugins install.jpg

安裝Dynamic Parameter插件
由于Dynamic Parameter插件有漏洞,在Jenkins中搜索不到,這里給出下載地址,經(jīng)測試目前只有0.1.1版本能用,該插件下載完成后需要在插件管理-高級-上傳插件中進(jìn)行安裝
插件安裝完成后,我們就來進(jìn)行基本環(huán)境的配置

2、環(huán)境配置
2.1系統(tǒng)配置:
Manage Jenkins.jpg

增加環(huán)境變量
進(jìn)入Manage Jenkins -> Configure System -> 全局屬性,勾選Environment variables,增加一對鍵值


PATH.jpg

其中PATH的值為本機(jī)的環(huán)境變量,可以在終端執(zhí)行以下命令查看,為一堆路徑:

echo $PATH

Android sdk配置:


Android sdk.jpg

SDK配置中的鍵必須是ANDROID_HOME,值為你本機(jī)的Android SDK目錄,這里要注意SDK目錄的權(quán)限問題,沒有權(quán)限的話,可能會導(dǎo)致后期構(gòu)建的時候提示找不到SDK路徑。
JDK、Git、Gradle配置:


Global Tool Configuration.jpg

jdk git gradle.jpg
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í)際情況設(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項(xiàng)目配置

新建項(xiàng)目


create new project.jpg

填寫項(xiàng)目名稱,這里我們選擇構(gòu)建一個自由風(fēng)格的軟件項(xiàng)目


create 2.jpg

創(chuàng)建好后,進(jìn)入項(xiàng)目配置
項(xiàng)目配置.jpg

選擇參數(shù)化構(gòu)建,添加參數(shù)GitParameter


參數(shù)化構(gòu)建_git.jpg

這里變量名隨意,參數(shù)類型選擇Branch or Tag
select branch.jpg

添加參數(shù),選擇Choice Parameter,這里參數(shù)名為IS_JENKINS,注意這里參數(shù)名要和gradle.properties中定義的相同
IS_JENKINS.jpg

創(chuàng)建參數(shù)BUILD_TYPE,這里名字可以隨意,參數(shù)根據(jù)自己項(xiàng)目中定義
BUILD_TYPE.jpg

添加Dynamic Parameter
Dynamic Parameter.jpg

創(chuàng)建參數(shù)BUILD_TIME,注意這里參數(shù)名要和gradle.properties中定義的相同
并且Dynamic Parameter使用的是Groovy Script


BUILD_TIME.jpg

創(chuàng)建
源碼管理
這里我們使用的是Git
添加gitlab賬號.jpg

輸入倉庫地址后,點(diǎn)擊添加,添加認(rèn)證
gitlab_jenkins.jpg

創(chuàng)建完成后,選擇剛剛創(chuàng)建的用戶憑據(jù),并填寫上面參數(shù)化構(gòu)建填好的分支變量名,注意變量名前要加$
源碼管理.jpg

選擇構(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


select gradle version.jpg

配置完成后,點(diǎn)擊保存,回到項(xiàng)目首頁


項(xiàng)目列表.jpg

可以看到,原先的立即構(gòu)建已經(jīng)變成了Build with Parameters
Build with Parameters.jpg

配置完成后,點(diǎn)擊開始構(gòu)建,這里我們可以查看控制臺輸出


控制臺輸出.jpg

在控制臺我們可以看到和Android Studio打包同樣的輸出結(jié)果,最后顯示構(gòu)建成功。
Build Successful.jpg

構(gòu)建完成后,我們可以在項(xiàng)目目錄找到打好的包
build apk.jpg

構(gòu)建名稱
原本的構(gòu)建名稱只是一個編號,對于使用人員來講,沒有辨識度,我們可以在項(xiàng)目的構(gòu)建環(huán)境中進(jìn)行配置,更改名稱,具體操作如下,在構(gòu)建環(huán)境中勾選Set Build Name,并填入上文配置的參數(shù)名稱
set build name 0.jpg

保存后,我們再次構(gòu)建查看下結(jié)果,構(gòu)建名稱已經(jīng)改變了


set build name.jpg

經(jīng)過如上配置,我們的Jenkins打包就可以正常工作了,但是構(gòu)建完成的包,測試人員該怎么安裝呢,不能每次打完包還要我們?nèi)ロ?xiàng)目目錄下找到發(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)容


download app.jpg
3.2 Python安裝及配置

安裝Python和pip詳見Mac安裝Python和pip
安裝Pillow
輸入命令sudo pip install Pillow,出現(xiàn)如下提示說明已經(jīng)安裝完成

install pillow

3.3 qrcode安裝及配置

輸入以下命令

pip3 install myqr
install myqr.jpg

以上配置完成后,打開Jenkins,進(jìn)入Manage Jenkins -> 全局屬性,然后新增屬性,添加Python全局變量


python env.jpg
3.4 生成二維碼

進(jìn)入項(xiàng)目 -> 配置 -> 構(gòu)建,增加構(gòu)建步驟


Execute shell.jpg

填寫如下命令

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ù)自己的實(shí)際情況進(jìn)行配置
關(guān)于qrcode的詳細(xì)使用,詳見Github

3.5 展示二維碼

通過myqr命令會在Tomcat下載目錄生成一張二維碼圖片,接下來我們要把二維碼圖片顯示在Jenkins上:
安裝插件description setter plugin,安裝好后,進(jìn)入項(xiàng)目->配置->構(gòu)建后操作,增加構(gòu)建后操作步驟,選擇Set build description

select set build description.jpg

我這里已經(jīng)設(shè)置過了build description,所以是灰色的
選擇完成后,描述可以添加HTML標(biāo)簽,所以我們可以將<img src='' />標(biāo)簽加入到描述中,不過這里有個問題,加了img標(biāo)簽后,Jenkins并不會顯示二維碼圖片,這是因?yàn)镴enkins出于安全考慮,所有描述信息的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ù)自身實(shí)際情況進(jìn)行配置。


build description.jpg
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é)果這個文件拉下來之后,還是報(bào)這個錯,后來估計(jì)是權(quán)限問題,然后將everyone只讀權(quán)限放在Library目錄下就沒問題了

參考文章:
Android-解放雙手告別測試-使用Jenkins自動化打包
Android使用Jenkins持續(xù)集成
Jenkins本地搭建遇到的問題 for Mac

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容