當 jenkins遇上Android Studio 3.0

wustor發表於2017-11-06

概述

簡介

jenkins是一個開源軟體專案,是基於Java開發的一種持續整合工具,用於監控持續重複的工作,旨在提供一個開放易用的軟體平臺,使軟體的持續整合變成可能。
關於Android Studio持續整合的文章已經是滿天飛了,不過都是在AS 2.X的環境下面進行整合的,最近升級了AS 3.0之後,發現按照之前的一些方法無法在AS 3.0上順利整合,本來很簡單的幾步操作,卻除錯了很久,下面簡單記錄一下除錯的過程,可以讓一部分開發者少走彎路。

本文是基於GitLab整合,其餘的類似於Git,SVN其實原理都一樣,稍微有點區別,稍微除錯一下就好。

正文

基本知識

簡單的Groovy語法

普通識別符號:以字母、美元符號$或下劃線_開始,不能以數字開始

def date = new Date()複製程式碼

引號識別符號:使用單引號括住的字串

def name = 'android'複製程式碼

括號{}:表示引用
美元符號$:表示拼接

assemble${PRODUCT_FLAVOR}${BUILD_TYPE}
//相當於assembleWandoujiaRelease複製程式碼

基本的gradle命令

說來慚愧,平時都是點選AS的視覺化按鈕,很少去研究這些命令,直到這次debug,才發現,命令列在debug確實很有用

  • gradlew clean //刪除app目錄下的build檔案
  • gradlew build //編譯debug跟release包
  • gradlew assembleDebug //編譯debug包
  • gradlew assembleRelease //編譯release包

jenkins提供的全域性變數

這些變數可以在寫指令碼,包括gradle指令碼以及python指令碼的時候可以呼叫,我們也可以自己通過外掛來配置一些變數,用來進行構建不同的渠道包,下面選取了一些常用的:

  • BRANCH_NAME :專案分支名稱
  • CHANGE_AUTHOR:修改專案的作者
  • BUILD_NUMBER:構建的序列號
  • JOB_NAME:構建的專案名稱
  • WORKSPACE:伺服器構建專案的位置
  • JENKINS_HOME:jenkins的根目錄
  • GIT_COMMITTER_EMAIL: Git提交作者的郵箱

登陸無效解決方案

當關掉jenkins的網頁,再重新開啟的時候,會讓你重新登入,但是當你輸入正確的使用者名稱跟密碼的時候,卻會提示你登陸無效,解決方案如下:

  • 1.找到安裝目錄下的config檔案
  • 2.找到useSecurity節點,將true改為false
    <useSecurity>false</useSecurity>複製程式碼
  • 3、找到authorizationStrategy跟securityRealm刪除
  • 4、重啟jenkins,不知道怎麼重啟的,直接重啟電腦,再次啟動jenkins即可。

修改build.gradle檔案

AS3.0升級了gradle,改動較大,對比一下之前的程式碼,便可以發現區別,主要是在flavor的新增時必須增加一個dimension,apk輸出路徑做了較大的修改,為了減少程式碼量,只貼出了變動的部分

android {
    signingConfigs {
        config {
            keyAlias 'demo'
            keyPassword '123456'
            storeFile file('chuangmei.jks')
            storePassword '123456'
            flavorDimensions "versionCode"
        }
    }

    flavorDimensions "market"
    productFlavors {
        xiaomi {
            dimension "market"
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
        }
        wandoujia {
            dimension "market"
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
        }

    }
    applicationVariants.all { variant ->
        variant.outputs.all { output ->
            outputFileName = variant.productFlavors[0].name + new Date().format('yyyyMMddHHmmss') + '-' + variant.buildType.name + '.apk'
        }
    }
}複製程式碼

安裝jenkins

官網是jenkins.io,有三種安裝方式,分別是war包,native包以及docker容器,這裡我選擇的是native包,因為這種方式可以幫助我們安裝一部分外掛,基本上可以滿足我們的需求。

配置環境

外掛安裝

native包安裝
基本夠用,然後可以根據需求擴充套件
war包安裝

  • Git plugin
  • Gradle Plugin
  • SSH plugin
    這裡選擇的幾個外掛貌似能夠打出apk,網上有很多文章,會安裝很多外掛,一來沒必要,二來很多外掛已經過時了,最新的jenkins版本已經不支援,即使是通過本地上傳的方式,除非使用較老的jenkins版本。

系統設定

設定構建路徑
找到主目錄,然後點選高階

修改構建路徑
修改構建路徑

理解了groovy語法,上面的路徑就很好理解了,這個是可以隨便修改的

設定環境變數
找到全域性屬性,勾選環境變數,設定SDK路徑

設定環境變數
設定環境變數

全域性工具配置

name可以隨便填,對應的value則是相應的路徑

JDK路徑

JDK路徑
JDK路徑

Git路徑
Git路徑
Git路徑

Gradle路徑
Gradle路徑
Gradle路徑

配置專案

建立一個專案

名稱隨便填,這裡選擇自由風格的軟體專案,選擇第一個也可以,看自己需求

建立專案
建立專案

配置專案

引數化構建

引數名稱 引數型別 引數值
BUILD_TYPE choice Build,Debug
PRODUCT_FLAVOR choice Xiaomi,Wandoujia

引數化構建
引數化構建

構建環境

配置相應的構建環境

構建環境
構建環境

構建

根據之前設定的條件進行編譯操作

構建
構建

構建後操作

主要是可以用來收集構建出來的apk以及相應的編譯檔案

構建後操作
構建後操作

開始構建

其實最花時間的還是這裡,因為,升級了AS 3.0之後,gradle的版本變成了4.1,改動特別大,所以升級要慎重,但是已經升級了,問題還是得解決。

執行 gradle clean assembleRelease

是從這一行程式碼進行報的錯,提示我說是在release合併資源的時候報錯了,下面是具體的資訊

下面最後一行開始報錯

:app:generateAndroidReleaseResValues
:app:generateAndroidReleaseResources
:app:mergeAndroidReleaseResources複製程式碼

然後錯誤一直不斷重複

AAPT err(Facade for 1119711668) : No Delegate set : lost message:\\?\C:\Windows\System32\config\systemprofile\.gradle\caches\transforms-1\files-1.1\appcompat-v7-25.4.0.aar\76d6a769daf730ed767830374ebcd3bd\res\drawable-hdpi-v4\abc_textfield_search_default_mtrl_alpha.9.png ERROR: Unable to open PNG file複製程式碼

追蹤堆疊資訊 --stacktrace --debug

* What went wrong:
16:04:41.129 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] Execution failed for task ':app:mergeAndroidReleaseResources'.
16:04:41.129 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] > Error: java.util.concurrent.ExecutionException: com.android.builder.internal.aapt.AaptException: 
16:04:41.129 [ERROR]複製程式碼

對吧,日誌都出來了,看地我是一臉懵逼,只看懂了app:mergeAndroidReleaseResources。

漫長的debug之路

環境檢查

我的專案在本地是成功編譯過的,不管是debug還是release都是OK的,clean也是沒問題的,所以我當時就覺得應該是服務端編譯的問題,但是服務端的程式碼是從gitlab上面獲取的,跟我本地的是一模一樣的,不應該有問題,難道是編譯環境出了問題嗎,因為上面的報錯都是跟gradle相關的,所以我就列印了各自的gradle:

本地grade版本:

Gradle 4.1
------------------------------------------------------------

Build time:   2017-08-07 14:38:48 UTC
Revision:     941559e020f6c357ebb08d5c67acdb858a3defc2

Groovy:       2.4.11
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_131 (Oracle Corporation 25.131-b11)
OS:           Windows 10 10.0 amd64複製程式碼

服務端grade版本:

Gradle 4.1
------------------------------------------------------------

Build time:   2017-08-07 14:38:48 UTC
Revision:     941559e020f6c357ebb08d5c67acdb858a3defc2

Groovy:       2.4.11
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_131 (Oracle Corporation 25.131-b11)
OS:           Windows 10 10.0 amd64複製程式碼

是一樣的,呵呵噠,只能再分析別的原因了,其實這個時候是沒有什麼思路的,因為太詭異了,基本上什麼都一樣了,但是服務端就是編譯不成功,我能怎麼辦,我也很絕望啊。然後就用Google搜尋了一下,搜到的答案基本上都不是太相關,只好作罷。

目錄對比

其實在對比環境之後當時就想著回退版本了,畢竟已經摺騰了很久了,不過轉念一想,程式設計師不就是為問題而生的嗎,然後又冷靜思考了一會兒,還有什麼是不一樣的,終於發現了,目錄,服務端編譯的目錄跟本地的目錄不一樣,可是這個在理論上是沒有影響的,不過值得一試。然後我就直接開啟了服務端在本地的專案,然後進行檢查。

gradle clean

本地

E:\Jenkins\workspace\Test>gradle clean
> Configure project :app
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:clean'.
> Unable to delete file: E:\Jenkins\workspace\Test\app\build\intermediates\merged-not-compiled-resources\android\release\anim\abc_fade_in.xml複製程式碼

服務端

[Gradle] - Launching build.
[Test] $ cmd.exe /C "C:\Users\pangchao\.gradle\wrapper\dists\gradle-4.1-all\bzyivzo6n839fup2jbap0tjew\gradle-4.1\bin\gradle.bat clean && exit %%ERRORLEVEL%%"
:clean
:app:clean

BUILD SUCCESSFUL in 20s
2 actionable tasks: 2 executed
Build step 'Invoke Gradle script' changed build result to SUCCESS複製程式碼

是不是很意外,一個失敗一個成功,而且居然是本地失敗,服務端成功,很詭異。

gradle assembleRelease

本地打包

E:\Jenkins\workspace\Test>gradle assembleMumayi
> Configure project :app
[E:\Jenkins\workspace\Test\app\build\outputs\mapping\mumayi\release\dump.txt]...
Removed unused resources: Binary resource data reduced from 559KB to 539KB: Removed 3%

BUILD SUCCESSFUL in 31s
51 actionable tasks: 49 executed, 2 up-to-date
E:\Jenkins\workspace\Test>複製程式碼

服務端打包

[Gradle] - Launching build.
:app:assembleMumayiRelease
:app:assembleMumayi
BUILD SUCCESSFUL in 7s
51 actionable tasks: 6 executed, 45 up-to-date
Build step 'Invoke Gradle script' changed build result to SUCCESS
Finished: SUCCESS複製程式碼

可以看到先由本地編譯,然後再經過服務端編譯時都可以成功的,但是之前我們是先經過服務端編譯時不能成功的,也就是說,我們要先進入到服務端的目錄本地成功編譯一次之後,才能在服務端成功編譯。最重要的一點是服務端打包之間不能進行clean,具體原因我也不是很清楚,應該是跟AS 3.0升級的特性有關係,改天研究一下官方文件才能知道具體原因,也就是說目前的情況如下:
也就是在AS3.0的基礎上,如果我想在服務端自動構建apk,那麼首先必須在本地成功編譯一次,既然是這樣,那麼我們乾脆在本地先把所有渠道的Debug包Release包先統一編譯一次,這樣的話在服務端不管想要打哪種包都是OK的,就這麼愉快地決定了。

  • gradlew clean
  • gradlew build

這樣就好了,剩餘的操作跟之前的AS 2.X基本一致,OK,到此為止,AS 3.0也可以很輕鬆的使用jenkins,雖然折騰了很久,但是最終還是解決了問題。

後續

整合過jenkins的小夥伴們可能知道,其實還有很多功能沒有細說,比如說打完包後將安裝包自動上傳到fir或者蒲公英,生成一個二維碼,傳送郵件到指定的收件人,其實這些都比較好解決的,網上已經有很多教程了,而且這些都可以通過外掛跟指令碼實現,下面貼一下相關的外掛,大家Google或者百度一下就可以搞定了,本文主要在於分析一下AS 3.0的繼承問題,沒有針對這些問題進行研究。

相關文章