Jenkins部署Android項(xiàng)目并上傳fir平臺(tái)及顯示二維碼下載

需求來(lái)源:項(xiàng)目提測(cè)后胡野,修改完bug總是需要自己手動(dòng)打包蛮浑,然后上傳到fir平臺(tái)上再通知測(cè)試下載安裝矿酵,浪費(fèi)了時(shí)間和人力婴梧,因此我們可以把Android項(xiàng)目部署到Jenkins平臺(tái)上,每次修改完bug后只需要在gitlib上提交下代碼辨绊,然后通知測(cè)試即可奶栖。

Jenkins安裝

下載地址:https://jenkins.io/zh/
安裝文檔:https://jenkins.io/zh/doc/pipeline/tour/getting-started/
按照默認(rèn)的推薦完成安裝即可。

Android環(huán)境

這些就不多說了门坷,本地的話應(yīng)該都有了宣鄙,如果部署到遠(yuǎn)程服務(wù)器上的話得安裝一下。

安裝Jenkins插件

Manage Jenkins >> Manage Plugins

  • 搜索安裝Git插件(遠(yuǎn)程倉(cāng)庫(kù))
  • 搜索安裝Git Parameter插件(配置選擇構(gòu)建分支)
  • 搜索安裝Gradle插件(Android構(gòu)建工具)
  • 搜索安裝description setter plugin插件(添加構(gòu)建后的描述)
  • 下載安裝fir插件(自動(dòng)上傳apk到fir平臺(tái))

也可以直接在https://plugins.jenkins.io/上搜索下載插件默蚌,然后在Jenkins插件管理上傳hpi文件冻晤。

上傳插件

配置環(huán)境參數(shù)
  • Manage Jenkins >> Configure System
    配置sdk和ndk環(huán)境變量

    配置ANDROID_HOMEANDROID_NDK_HOME環(huán)境變量,即sdk和ndk的安裝路徑绸吸,然后保存鼻弧。
  • Manage Jenkins >> Global Tool Configuration
    配置jdk,git和gradle環(huán)境

    配置jdkgit锦茁,gradle的安裝路徑攘轩,git的安裝路徑在Windows上填git.exe的路徑,比如:git\bin\git.exe码俩,linux上填git/bin/git度帮,然后保存。
  • Manage Jenkins >> Configure Global Security
    選擇safe html

    選擇Safe Html(描述等可使用html標(biāo)簽)稿存,然后保存笨篷。
創(chuàng)建項(xiàng)目
創(chuàng)建項(xiàng)目

創(chuàng)建一個(gè)自由風(fēng)格的項(xiàng)目,然后OK瓣履,進(jìn)入項(xiàng)目配置頁(yè)面率翅。


config

config

config

config

config

config

config

config
基本的配置就是這些了,大家可以根據(jù)自己的需求來(lái)進(jìn)行配置拂苹。
下面,我們來(lái)進(jìn)行打包:
開始構(gòu)建

打包成功

掃描二維碼或點(diǎn)擊下載鏈接痰洒,就會(huì)直接跳轉(zhuǎn)到fir的下載地址了瓢棒,這個(gè)下載地址是攜帶release_id的,每次構(gòu)建后的下載地址都是唯一的丘喻,而不是fir的短鏈接脯宿,使用短鏈接的話直接自己生成一個(gè)二維碼放在項(xiàng)目描述里面就行了,不用這么麻煩泉粉。
jenkins.sh

#!/bin/bash
# Jenkins打包時(shí)會(huì)執(zhí)行此腳本连霉,將buildId生成在一個(gè)本地文件中
fileName="jenkins_build_id.properties"
touch ${fileName}
echo "文件創(chuàng)建成功"
true > ${fileName}
echo "BUILD_ID=$1" >> ${fileName}

使用腳本將構(gòu)建id寫入項(xiàng)目文件里(windows也可使用python)榴芳,打包前可以執(zhí)行腳本做不少事呢可真是。
jenkins-build.gradle

import groovy.json.JsonSlurper

/**
 * 獲取Jenkins打包ID
 * @return
 */
String getJenkinsBuild() {
    def buildFile = project.rootProject.file("jenkins_build_id.properties")
    if (buildFile.exists()) {
        def buildFileProperties = new Properties()
        def fis = new FileInputStream(buildFile)
        buildFileProperties.load(fis)
        def id = buildFileProperties['BUILD_ID']
        fis.close()
        return id
    } else {
        return null
    }
}

/**
 * 刪除Jenkins打包生成的文件
 */
void deletedJenkinsBuildFile() {
    def filePath = project.rootProject.file("jenkins_build_id.properties").absolutePath
    def buildFile = new File(filePath)
    if (buildFile.exists()) {
        buildFile.delete()
    }
}

/**
 * 上傳打包完成后的apk到fir平臺(tái)上,并上傳BUILD_ID和下載地址到服務(wù)器上
 */
task uploadApk2Fir() {
    doLast {
    //根據(jù)自己的apk路徑填寫
        def sb = new StringBuilder()
        sb.append(project.buildDir)
                .append(File.separator)
                .append("outputs")
                .append(File.separator)
                .append("apk")
                .append(File.separator)
                .append("release")
                .append(File.separator)
                .append("AndResGuard_app-release")
                .append(File.separator)
                .append("app-release_7zip_aligned_signed.apk")
        def filePath = sb.toString()
        println "apk路徑:$filePath"
        def apkFile = new File(filePath)
        def buildId = getJenkinsBuild()
        if (buildId == null || !apkFile.exists()) {
            println "未找到打包id或apk"
            return
        }
        println "即將上傳 $apkFile 到fir"
        //fir平臺(tái)的應(yīng)用ID跺撼,可在應(yīng)用詳情里查看
        def FIR_APP_ID = "xxxxxxxxxxxxxxxxx"
        //fir平臺(tái)的應(yīng)用API TOKEN
        def FIR_API_TOKEN = "xxxxxxxxxxxxxxxxx"
        def packageName = "com.android.test"
        def appInfo = ("curl -X POST -d type=android&bundle_id=$packageName&api_token=$FIR_API_TOKEN http://api.fir.im/apps").execute().text
        def appInfoBean = new JsonSlurper().parseText(appInfo)
        def token = appInfoBean["cert"]["binary"]["token"]
        def key = appInfoBean["cert"]["binary"]["key"]
        def url = appInfoBean["cert"]["binary"]["upload_url"]
        def log = "BUILD_ID:${buildId},Upload-By-Jenkins!!!"
        def versionName = project.versionName.toString()
        def versionCode = project.versionCode.toInteger()
        def upExecute = new StringBuilder()
                .append("curl -X POST --form file=@$apkFile")
                .append(" -F token=$token")
                .append(" -F key=$key")
                .append(" -F x:name=test")
                .append(" -F x:version=$versionName")
                .append(" -F x:build=$buildId")
                .append(" -F x:changelog=$log")
                .append(" $url").toString()
        def uploadInfo = (upExecute).execute().text
        def uploadResultBean = new JsonSlurper().parseText(uploadInfo)
        def isCompleted = uploadResultBean["is_completed"]
        if (isCompleted) {
            println "上傳 $apkFile 完成"
            def apkInfoText = ("curl -X GET -d api_token=$FIR_API_TOKEN http://api.fir.im/apps/$FIR_APP_ID").execute().text
            def apkInfoBean = new JsonSlurper().parseText(apkInfoText)
            def masterReleaseId = apkInfoBean["master_release_id"]
            println "獲取到最新releaseId為$masterReleaseId"
            //上傳成功后可獲取到apk的release_id,將build_id和release_id一起存入到數(shù)據(jù)庫(kù)表中
            def params = "buildId=$buildId&releaseId=$masterReleaseId&versionCode=$versionCode&versionName=$versionName&type=test"
            URL upUrl = new URL("http://test.api/test_jenkins.php?$params")
            HttpURLConnection conn = (HttpURLConnection) upUrl.openConnection()
            conn.setRequestMethod("GET")
            conn.connect()
            def responseCode = conn.getResponseCode()
            if (responseCode == HttpURLConnection.HTTP_OK) {
                println "更新上傳信息完成"
            } else {
                println "更新上傳信息失敗"
            }
        } else {
            println "上傳 $apkFile 失敗"
        }
        deletedJenkinsBuildFile()
    }
}

上傳apk的task任務(wù)窟感,也可以根據(jù)這個(gè)來(lái)修改一下將uploadApk2Fir加入到android構(gòu)建完成后自動(dòng)執(zhí)行,也就是不通過Jenkins打包歉井,手動(dòng)打包后上傳apk到fir平臺(tái)上面柿祈。
gradle.properties

# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.useAndroidX=true
android.enableJetifier=true
android.useDeprecatedNdk=true
#項(xiàng)目的配置參數(shù)
#項(xiàng)目版本號(hào)
versionCode=100
#項(xiàng)目版本名稱
versionName=6.0.2
#最低SDK版本
minSdkVersion=16
#目標(biāo)SDK版本
targetSdkVersion=28
#編譯運(yùn)行版本
compileSdkVersion=28
#是否可配置運(yùn)行環(huán)境
CAN_CHANGE_NET=true
#運(yùn)行環(huán)境
BASE_URL=http://android.test.api/

在Jenkins中配置的選擇參數(shù)會(huì)替換這里面的值,所以可以配置一些需要的變量參數(shù)在項(xiàng)目里使用哩至。

介紹完了躏嚎,當(dāng)然Jenkins的功能不止這些,有興趣的自己摸索菩貌。后面還部署了一個(gè)Android原生和Flutter的混合項(xiàng)目卢佣,踩坑太多都是淚,最后終于搭建成功箭阶。有問題的同學(xué)可以評(píng)論虚茶,看到了我會(huì)回復(fù)的。
那個(gè)尾膊,參考鏈接具體是哪些都忘了媳危,部署的時(shí)間有點(diǎn)長(zhǎng)了,現(xiàn)在百度搜一下一大堆冈敛,找不到原來(lái)借鑒的了待笑,在此感謝廣大的開發(fā)者及開源分享精神。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抓谴,一起剝皮案震驚了整個(gè)濱河市暮蹂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌癌压,老刑警劉巖仰泻,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滩届,居然都是意外死亡集侯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門帜消,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)棠枉,“玉大人,你說我怎么就攤上這事泡挺”惭龋” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵娄猫,是天一觀的道長(zhǎng)贱除。 經(jīng)常有香客問我生闲,道長(zhǎng),這世上最難降的妖魔是什么月幌? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任碍讯,我火速辦了婚禮,結(jié)果婚禮上飞醉,老公的妹妹穿的比我還像新娘冲茸。我一直安慰自己,他們只是感情好缅帘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布轴术。 她就那樣靜靜地躺著,像睡著了一般钦无。 火紅的嫁衣襯著肌膚如雪逗栽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天失暂,我揣著相機(jī)與錄音彼宠,去河邊找鬼。 笑死弟塞,一個(gè)胖子當(dāng)著我的面吹牛凭峡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播决记,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼摧冀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了系宫?” 一聲冷哼從身側(cè)響起索昂,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扩借,沒想到半個(gè)月后椒惨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潮罪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年康谆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫉到。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沃暗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屯碴,到底是詐尸還是另有隱情描睦,我是刑警寧澤膊存,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布导而,位于F島的核電站忱叭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏今艺。R本人自食惡果不足惜韵丑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虚缎。 院中可真熱鬧撵彻,春花似錦、人聲如沸实牡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)创坞。三九已至碗短,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間题涨,已是汗流浹背偎谁。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纲堵,地道東北人巡雨。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像席函,于是被迫代替她去往敵國(guó)和親铐望。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容