用 Jenkins 自動化構建 Android 和 iOS 應用

ctarol 8年前發布 | 27K 次閱讀 Java IOS Android Android開發 移動開發

來自: http://android.jobbole.com/82423/

背景

隨著業務需求的演進,工程的復雜度會逐漸增加,自動化的踐行日益強烈。事實上,工程的自動化一直是我們努力的目標,能有效提高我們的生產效率,最大化減少人為出錯的概率,實現一些復雜的業務需求應變。

場景如下,公司現在的測試人員每次需要測試新版本,都需要開發人員打包,放到ftp,測試人員然后從ftp上拷貝到本地(或者用手機的ES文件管理器),再安裝。尤其臨近發版的一周,幾乎每天都要新版本。這樣的話,有兩方面的影響:第一,打斷了開發人員的開發進度;第二,開發人員打包效率低下,尤其是ios,不順的話,總是打的不對(可能是證書的問題)。

要解決這個問題,必須實現移動端應用的自動化構建。具體說來就是,使用持續集成(CI)系統jenkins,自動檢測并拉取最新代碼,自動打包android的apk和ios的ipa,自動上傳到內測分發平臺蒲公英上。(接下來,測試人員只要打開一個(或多個)固定的網址,掃描一下二維碼,就能下載最新的版本了…)

環境

因為要編譯ios,所以選擇Mac OSX 10.11.1。無論是哪個操作系統,jenkins的配置是一樣的。

安裝Jenkins

官網地址: http://jenkins-ci.org/

Java

// 使用brew安裝
brew install jenkins
// 啟動,直接運行jenkins即可啟動服務
jenkins
// 使用brew安裝
brewinstalljenkins
// 啟動,直接運行jenkins即可啟動服務
jenkins
</div>

默認訪問 http://localhost:8080/ , 可進入jenkins配置頁面。

安裝Jenkins相關插件

點擊系統管理>管理插件>可選插件,可搜索以下插件安裝

git插件(GIT plugin)

ssh插件(SSH Credentials Plugin)

Gradle插件(Gradle plugin) – android專用

Xcode插件(Xcode integration) – ios專用

</div>

新建Job

主頁面,新建 -> 構建一個自由風格的軟件項目即可。對于類似的項目,可以選擇 -> 復制已有的Item,要復制的任務名稱里輸入其他job的首字符會有智能提示。

配置git倉庫

如果安裝了git插件,在源碼管理會出現Git,選中之后:

Repositories -> https://github.com/openproject/ganchai , 如果是ssh還要配置Credentials。

Branch -> */master,選定一個要編譯的分支代碼。

如下:

如果是私有的倉庫(比如git://xxxxx.git),點擊Credentials – Add,彈出對話框,配置sshkey最簡單了:

配置自動拉取最新代碼

在構建觸發器中,有兩種自動拉取代碼并編譯的策略:

  1. 設置Poll SCM,設置定時器,定時檢查代碼更新,有更新則編譯,否則不編譯(我暫時用的是這個)。
  2. 也可以設置Build periodically,周期性的執行編譯任務。

關于定時器的格式,我只能從網上摘抄一段稍微靠譜一點的說明:

This field follows the syntax of cron (with minor differences). Specifically, each line consists of 5 fields separated by TAB or whitespace:

MINUTE HOUR DOM MONTH DOW

MINUTE Minutes within the hour (0-59) HOUR The hour of the day (0-23) DOM The day of the month (1-31) MONTH The month (1-12) DOW The day of the week (0-7) where 0 and 7 are Sunday.

To specify multiple values for one field, the following operators are available. In the order of precedence,

  • '*' can be used to specify all valid values.
  • 'M-N' can be used to specify a range, such as "1-5"
  • 'M-N/X' or ' /X' can be used to specify skips of X''s value through the range, such as " /15" in the MINUTE field for "0,15,30,45" and "1-6/2" for "1,3,5"
  • 'A,B,...,Z' can be used to specify multiple values, such as "0,30" or "1,3,5"

Empty lines and lines that start with '#' will be ignored as comments. In addition, @yearly, @annually, @monthly, @weekly, @daily, @midnight, @hourly are supported.

This fieldfollowsthesyntaxofcron (withminordifferences). Specifically, each lineconsistsof 5 fieldsseparatedbyTABor whitespace:
 
MINUTEHOURDOMMONTHDOW
 
MINUTEMinuteswithinthehour (0-59)
HOURThehouroftheday (0-23)
DOMThedayofthemonth (1-31)
MONTHThemonth (1-12)
DOWThedayoftheweek (0-7) where 0 and 7 areSunday.
 
To specifymultiplevaluesfor onefield, thefollowingoperatorsareavailable. In theorderofprecedence,
 
  * '*' canbeusedto specifyallvalidvalues.
  * 'M-N' canbeusedto specify a range, suchas "1-5"
  * 'M-N/X' or '*/X' canbeusedto specifyskipsof X''s valuethroughtherange, suchas "*/15" in theMINUTEfieldfor "0,15,30,45" and "1-6/2" for "1,3,5"
  * 'A,B,...,Z' canbeusedto specifymultiplevalues, suchas "0,30" or "1,3,5"
 
Emptylinesand linesthatstartwith '#' willbeignoredas comments.
In addition, @yearly, @annually, @monthly, @weekly, @daily, @midnight, @hourlyaresupported.
</div>

舉兩個例子:

Java

// every minute


// every 5 mins past the hour 5 </pre>

// every minute


// every 5 mins past the hour 5 </pre> </div>

配置gradle – android專用

請ios的朋友們請飄過.

如果安裝gradle插件成功的話,應該會出現下圖的Invoke Gradle script,配置一下:

${WORKSPACE}表示當前job下的workspace目錄,主要是存放代碼。更多的環境變量請參考文末附錄。

這樣,就能自動在project下的app的build/outputs/apk下生成相應的apk.

編譯失敗?可能要解決以下2個問題:

  1. gradle沒配置環境變量。
    比如我在/etc/profile中配置一下GRADLE_HOME: 

    Java

    export GRADLE_HOME='/home/jay/.gradle/wrapper/dists/gradle-2.2.1-all/c64ydeuardnfqctvr1gm30w53/gradle-2.2.1'
    export PATH=$GRADLE_HOME/bin:$PATH
    exportGRADLE_HOME='/home/jay/.gradle/wrapper/dists/gradle-2.2.1-all/c64ydeuardnfqctvr1gm30w53/gradle-2.2.1'
    exportPATH=$GRADLE_HOME/bin:$PATH
  2. 找不到local.properties中sdk定義。
    因為一般來說local.properties不會添加到版本庫。
    所以需要手動copy到${WORKSPACE}下的Project目錄下(可參考自己Android Studio工程結構)。
    關于local.properties的定義,這里記錄一下,做個備份: 

    Java

    sdk.dir=xx/xx/android-sdk
    sdk.dir=xx/xx/android-sdk

再編譯一般就會編譯成功,當然當那些第三方庫需要重新下載的話,編譯可能會很慢。

配置xcode – ios專用

請android的同學們飄過。

安裝Xcode插件后,可看到如下圖界面,并配置:

這里有兩個地方需要注意。

  1. 簽名
  2. 需要Shared Schema文件.

上傳到蒲公英平臺

在官網文檔里有說明,通過linux平臺上傳app的關鍵代碼

Java

curl -F "file=@/tmp/example.ipa" -F "uKey=" -F "_api_key=" http://www.pgyer.com/apiv1/app/upload
curl -F "file=@/tmp/example.ipa" -F "uKey=" -F "_api_key=" http://www.pgyer.com/apiv1/app/upload
</div>

具體來說,

Java

# 先把${version}看成v1.0吧
curl -F "file=@/home/xxx/release/ganchai-release-${version}-0101-dev.apk" -F "uKey=231xxxxe6" -F "_api_key=0xxxx499" -F "publishRange=2" http://www.pgyer.com/apiv1/app/upload
# 先把${version}看成v1.0吧
curl -F "file=@/home/xxx/release/ganchai-release-${version}-0101-dev.apk" -F "uKey=231xxxxe6" -F "_api_key=0xxxx499" -F "publishRange=2" http://www.pgyer.com/apiv1/app/upload
</div>

這樣就完成一個app上傳到蒲公英了。

實際上,我們可能會面對更復雜的場景,比如上面的${version}, 而version定義于build.gradle如下:

Java

ext {
    compileSdkVersion = 22
    buildToolsVersion = "23.0.1"
    minSdkVersion = 10
    targetSdkVersion = 22
    versionCode = 1111
    versionName = "v1.2.0.0"
}
ext {
    compileSdkVersion = 22
    buildToolsVersion = "23.0.1"
    minSdkVersion = 10
    targetSdkVersion = 22
    versionCode = 1111
    versionName = "v1.2.0.0"
}
</div>

得想辦法讀到versionName, 然后拼出最終的文件名,這樣下次版本升級了之后也能動態的上傳app到蒲公英了。

Java

# 使用sed命令讀取,使用cut切割,最終動態讀取到versionName
version=`sed -n '21,1p' ${WORKSPACE}/xxx/build.gradle | cut -c20-27`
# 使用sed命令讀取,使用cut切割,最終動態讀取到versionName
version=`sed -n '21,1p' ${WORKSPACE}/xxx/build.gradle | cut -c20-27`
</div>

這是android的apk上傳過程,相應的,ios是上傳ipa,方法是一樣的,不再贅述。

小結

把開發人員發布版本的工作自動化之后,如此一來,方便了測試人員隨時拉取并構建最新版本,更解放了開發人員自己的發版本的工作,一個字,善!

附錄

jenkins中定義的那些環境變量:

The following variables are available to shell scripts

BUILD NUMBER The current build number, such as "153" BUILD ID The current build id, such as "2005-08-22 23-59-59" (YYYY-MM-DD hh-mm-ss) BUILD DISPLAY NAME The display name of the current build, which is something like "#153" by default. JOB NAME Name of the project of this build, such as "foo" or "foo/bar". (To strip off folder paths from a Bourne shell script, try: ${JOB NAME##*/}) BUILD TAG String of "jenkins-${JOB NAME}-${BUILD NUMBER}". Convenient to put into a resource file, a jar file, etc for easier identification. EXECUTOR NUMBER The unique number that identifies the current executor (among executors of the same machine) that’s carrying out this build. This is the number you see in the "build executor status", except that the number starts from 0, not 1. NODE NAME Name of the slave if the build is on a slave, or "master" if run on master NODE LABELS Whitespace-separated list of labels that the node is assigned. WORKSPACE The absolute path of the directory assigned to the build as a workspace. JENKINS HOME The absolute path of the directory assigned on the master node for Jenkins to store data. JENKINS URL Full URL of Jenkins, like http://server:port/jenkins/ (note: only available if Jenkins URL set in system configuration) BUILD URL Full URL of this build, like http://server:port/jenkins/job/foo/15/ (Jenkins URL must be set) JOB URL Full URL of this job, like http://server:port/jenkins/job/foo/ (Jenkins URL must be set) SVN REVISION Subversion revision number that's currently checked out to the workspace, such as "12345" SVN URL Subversion URL that's currently checked out to the workspace.

Thefollowingvariablesareavailableto shellscripts
 
BUILD_NUMBER
Thecurrentbuildnumber, suchas "153"
BUILD_ID
Thecurrentbuildid, suchas "2005-08-22_23-59-59" (YYYY-MM-DD_hh-mm-ss)
BUILD_DISPLAY_NAME
Thedisplaynameofthecurrentbuild, whichis somethinglike "#153" bydefault.
JOB_NAME
Nameoftheprojectofthis build, suchas "foo" or "foo/bar". (To stripofffolderpathsfrom a Bourneshellscript, try: ${JOB_NAME##*/})
BUILD_TAG
String of "jenkins-${JOB_NAME}-${BUILD_NUMBER}". Convenientto putinto a resourcefile, a jarfile, etcfor easieridentification.
EXECUTOR_NUMBER
Theuniquenumberthatidentifiesthecurrentexecutor (amongexecutorsofthesamemachine) that’s carryingoutthis build. This is thenumberyouseein the "build executor status", exceptthatthenumberstartsfrom 0, not 1.
NODE_NAME
Nameoftheslaveif thebuildis on a slave, or "master" if runonmaster
NODE_LABELS
Whitespace-separatedlistoflabelsthatthenodeis assigned.
WORKSPACE
Theabsolutepathofthedirectoryassignedto thebuildas a workspace.
JENKINS_HOME
Theabsolutepathofthedirectoryassignedonthemasternodefor Jenkinsto storedata.
JENKINS_URL
FullURLofJenkins, likehttp://server:port/jenkins/ (note: only available if Jenkins URL set in system configuration)
BUILD_URL
FullURLofthis build, likehttp://server:port/jenkins/job/foo/15/ (Jenkins URL must be set)
JOB_URL
FullURLofthis job, likehttp://server:port/jenkins/job/foo/ (Jenkins URL must be set)
SVN_REVISION
Subversionrevisionnumberthat's currently checked out to the workspace, such as "12345"
SVN_URL
Subversion URL that's currentlycheckedoutto theworkspace.
</div> </div>

 本文由用戶 ctarol 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!