Jenkins實現(xiàn)持續(xù)集成-使用Ant腳本構(gòu)建ios項目

原文 : tomczhen的博客

在Jenkins平臺上使用Ant腳本構(gòu)建iOS項目, 可以做到自動構(gòu)建打包, 上傳蒲公英, 構(gòu)建完成發(fā)送微信通知 等功能. 發(fā)布開發(fā)者進(jìn)行測試分發(fā),只需要點一下即可完成一系列操作.

Jenkins構(gòu)建面板
Jenkins構(gòu)建面板

安裝CocoaPods

  1. 更新Ruby

     gem update --system
    
  2. 修改Ruby安裝源

     gem sources --remove https://rubygems.org/
     gem sources -a https://ruby.taobao.org/
    

    注:使用gem sources -l命令查看源列表

  3. 安裝cocoapods

     sudo gem install cocoapods
    

xctool安裝

  1. 安裝brew

     ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    
  2. 安裝Xcode’s Command Line Tools

     xcode-select install
    
  3. 使用brew安裝xctool

     brew install xctool
    

安裝Apache Ant

  • brew install ant
    

配置運行環(huán)境

在Jenkins的系統(tǒng)設(shè)置頁面設(shè)置ANT_HOME路徑后裸,并在全局屬性中添加以下鍵值。

  • LANGzh_CN.UTF-8
  • PATH系統(tǒng)PATH輸出值

編寫Ant腳本

  • 官方手冊,可以查看可用的任務(wù)或命令以及具體的參數(shù)振惰,實例童社。

添加Jenkins任務(wù)

  • 新建任務(wù)類型”構(gòu)建一個自由風(fēng)格的軟件項目”揣炕,選擇源代碼管理的方式屏歹。在”增加構(gòu)建步驟”中選擇Invoke Ant,打開高級選項认臊,配置好Targets寨辩,在Build File中輸入配置好的build.xml文件路徑吓懈。

  • 注意:可以在任務(wù)中只配置源代碼管理,執(zhí)行任務(wù)測試獲取代碼是否正常靡狞,然后在服務(wù)器上的命令行界面中使用ant命令調(diào)用項目中的build.xml文件進(jìn)行編譯測試耻警。


腳本實例

腳本說明:

  • ant腳本本質(zhì)上還是通過調(diào)用xctool或xcodebuild命令進(jìn)行iOS打包
<?xml version="1.0" encoding="UTF-8"?>
<project name="antProjectName" default="config.dev, modify, pack, pgy">
    <property name="project.name" value="projectName" />
    <property name="project.workspace.name" value="workspaceName" />
    <property name="project.scheme.name" value="schemeName" />
    
    <exec executable="date" outputproperty="build.time" failifexecutionfails="false" errorproperty="DateError">
        <arg value="+%Y%m%d%H%M" />
    </exec>
    
    <!-- 獲取當(dāng)前 Git 版本控制信息 -->
    <exec executable="git" outputproperty="git.tag" failifexecutionfails="false" errorproperty="GitTagError">
        <arg value="describe" />
        <arg value="--tags" />
    </exec>
    <exec executable="git" outputproperty="git.commit" failifexecutionfails="false" errorproperty="GitCommitError">
        <arg value="log" />
        <arg value="--pretty=format:%h" />
        <arg value="-n1" />
    </exec>
    <exec executable="git" outputproperty="git.log" failifexecutionfails="false" errorproperty="GitLogError">
        <arg value="log" />
        <arg value="--pretty=format:%B" />
        <arg value="-n1" />
    </exec>
    <target name="config.dev">
        <property name="project.profile.name" value="adhoc" />
        <!-- 構(gòu)建開發(fā)環(huán)境配置信息 -->
        <property name="build.type" value="Dev" />
        <property name="build.config.name" value="開發(fā)環(huán)境" />
        <property name="build.file.path" value="dev" />
        <property name="build.file.name" value="${project.name}_${git.tag}_beta${build.time}" />
        <property name="pgy.description" value="LAN-Beta${line.separator}Tag:${git.tag} Commit:${git.commit}${line.separator}${git.log}" />
        <!-- 接口服務(wù)器地址 -->
        <property name="api.url" value="http://api.dev.server.com" />
        <!-- 第三方 Key -->
            <!-- 融云 -->
            <property name="key.rongcloud" value="rongcloud" />
            <property name="key.rongcloud_scid" value="rongcloud_scid" />
        
        <property name="build.file.url" value="http://www.pgyer.com/pgy" />
        <property name="build.file.qrcode" value="http://www.pgyer.com/app/qrcode/pgy" />
    </target>
    
    <target name="config.beta">
        <property name="project.profile.name" value="adhoc" />
        <!-- 構(gòu)建測試環(huán)境配置信息 -->
        <property name="build.type" value="Beta" />
        <property name="build.config.name" value="測試環(huán)境" />
        <property name="build.file.path" value="beta" />
        <property name="build.file.name" value="${project.name}_${git.tag}_beta${build.time}" />
        <property name="pgy.description" value="Beta${line.separator}Tag:${git.tag} Commit:${git.commit}${line.separator}${git.log}" />
        <!-- 接口服務(wù)器地址 -->
        <property name="api.url" value="http://api.beta.server.com" />
        <!-- 第三方 Key -->
            <!-- 融云 -->
            <property name="key.rongcloud" value="rongcloud" />
            <property name="key.rongcloud_scid" value="rongcloud_scid" />
        
        <property name="build.file.url" value="http://www.pgyer.com/pgy" />
        <property name="build.file.qrcode" value="http://www.pgyer.com/app/qrcode/pgy" />
    </target>
    
    <target name="config.release">
        <property name="project.profile.name" value="adhoc" />
        <!-- 構(gòu)建生產(chǎn)環(huán)境配置信息 -->
        <property name="build.type" value="Release" />
        <property name="build.config.name" value="生產(chǎn)環(huán)境-蒲公英" />
        <property name="build.file.path" value="release" />
        <property name="build.file.name" value="${project.name}_${git.tag}_r${build.time}" />
        <property name="pgy.description" value="Release${line.separator}Tag:${git.tag} Commit:${git.commit}${line.separator}${git.log}" />
        <!-- 接口服務(wù)器地址 -->
        <property name="api.url" value="http://api.release.server.com" />
        <!-- 第三方 Key -->
            <!-- 融云 -->
            <property name="key.rongcloud" value="rongcloud" />
            <property name="key.rongcloud_scid" value="rongcloud_scid" />
        
        <property name="build.file.url" value="http://www.pgyer.com/pgy" />
        <property name="build.file.qrcode" value="http://www.pgyer.com/app/qrcode/pgy" />
    </target>
    
    <target name="config.appstore">
        <property name="project.profile.name" value="appstore" />

        <!-- 構(gòu)建正式環(huán)境配置信息 -->
        <property name="build.type" value="AppStore" />
        <property name="build.config.name" value="正式環(huán)境-AppStore" />
        <property name="build.file.path" value="release" />
        <property name="build.file.name" value="${project.name}_${git.tag}_r${build.time}" />
        <property name="pgy.description" value="Release${line.separator}Tag:${git.tag} Commit:${git.commit}${line.separator}${git.log}" />
        <!-- 接口服務(wù)器地址 -->
        <property name="api.url" value="http://api.release.server.com" />
        <!-- 第三方 Key -->
        <!-- 融云 -->
        <property name="key.rongcloud" value="rongcloud" />
        <property name="key.rongcloud_scid" value="rongcloud_scid" />
        
        <property name="build.file.url" value="http://download.server.com/IOS/${project.scheme.name}/${build.file.name}.ipa" />
        <!-- 二維碼圖片接口 -->
        <property name="build.file.qrcode" value="http://qr.liantu.com/api.php?text=${build.file.url}" />
    </target>
    
    <target name="modify">
    <!-- 配置接口服務(wù)器地址 -->
        <!-- API Center Url -->
        <replaceregexp byline="true" encoding="UTF-8">
            <regexp pattern='^NSString\s+\*\s+const\s+APIServerUrl\s+=\s+(.*)' />
            <substitution expression='NSString * const APIServerUrl = @"${api.url}";' />
            <fileset dir="ProjectPath/Classes/Main/Other" includes="Const.m" />
        </replaceregexp>
    <!-- 配置第三方 Key -->
        <!-- 融云 Key -->
        <replaceregexp byline="true" encoding="UTF-8">
            <regexp pattern='^#define\s+RONGCLOUD_IM_APPKEY\s+(.*)' />
            <substitution expression='#define RONGCLOUD_IM_APPKEY @"${key.rongcloud}"' />
            <fileset dir="ProjectPath/Classes/Main/Other" includes="Const.h" />
        </replaceregexp>
        <!-- 融云客服 ID -->
        <replaceregexp byline="true" encoding="UTF-8">
            <regexp pattern='^#define\s+CustomerServiceId\s+(.*)' />
            <substitution expression='#define CustomerServiceId @"${key.rongcloud_scid}"' />
            <fileset dir="ProjectPath/Classes/Main/Other" includes="Const.h" />
        </replaceregexp>
        
    </target>
    
    <target name="pack">
        <property name="build.archive.path" value="build/${build.file.path}/${build.file.name}" />
        <!-- pod install-->
        <exec executable="pod" failonerror="true">
            <arg value="install" />
            <arg value="--verbose" />
            <arg value="--no-repo-update" />
        </exec>
        <!-- Clean -->
        <exec executable="xctool" failonerror="true">
            <arg value="-workspace" />
            <arg value="${project.workspace.name}.xcworkspace" />
            <arg value="-scheme" />
            <arg value="${project.scheme.name}" />
            <arg value="clean" />
        </exec>
        <!-- archive -->
        <exec executable="xctool" failonerror="true">
            <arg value="-workspace" />
            <arg value="${project.workspace.name}.xcworkspace" />
            <arg value="-scheme" />
            <arg value="${project.scheme.name}" />
            <arg value="build" />
            <arg value="archive" />
            <arg value="-archivePath" />
            <arg value="${build.archive.path}" />
        </exec>
        <!-- export ipa -->
        <exec executable="xcodebuild" failonerror="true">
            <arg value="-exportArchive" />
            <arg value="-archivePath" />
            <arg value="${build.archive.path}.xcarchive" />
            <arg value="-exportPath" />
            <arg value="${build.archive.path}.ipa" />
            <arg value="-exportFormat" />
            <arg value="ipa" />
            <arg value="-exportProvisioningProfile" />
            <arg value="${project.profile.name}" />
        </exec>
        
        <condition property="isAppStore">
            <equals arg1="${build.type}" arg2="AppStore" />
        </condition>
        <antcall target="copy2share" />
        <antcall target="pgy" />
    </target>
    
    <target name="copy2share" if="isAppStore">
        <!-- 輸出文件到指定目錄 -->
        <copy file="${basedir}/${build.archive.path}.ipa" todir="/Jenkins/IOS/${project.scheme.name}/" />
    </target>

    <target name="pgy" unless="isAppStore">
    <!-- 發(fā)布到蒲公英平臺 -->
        <property name="pgy.ukey" value="pgyukey" />
        <property name="pgy.api_key" value="pgyapi_key" />
        <exec executable="curl" outputproperty="pgy.json" failifexecutionfails="true">
            <arg value="-s" />
            <arg value="-F" />
            <arg value="file=@${basedir}/${build.archive.path}.ipa" />
            <arg value="-F" />
            <arg value="uKey=${pgy.ukey}" />
            <arg value="-F" />
            <arg value="_api_key=${pgy.api_key}" />
            <arg value="-F" />
            <arg value="updateDescription=${pgy.description}" />
            <arg value="http://www.pgyer.com/apiv1/app/upload"/>
        </exec>
        <echo message="${pgy.json}" />
    </target>
    
    <target name="wechat">
        <exec executable="date" outputproperty="build.report.time" failifexecutionfails="false" errorproperty="DateError">
            <arg value="+%Y年%m月%d日%H:%M" />
        </exec>
        <!-- corp.media 需要使用永久素材ID -->
        <!--
        wechat.agentid 微信企業(yè)號應(yīng)用id
            wechat.partyid 接收部門ID
            wechat.userid  接收用戶ID
        -->
        <property name="wechat.partyid" value="2" />
        <property name="wechat.agentid" value="12" />
        <property name="wechat.userid" value="" />
        <!-- 獲取微信API Token -->
        <property name="corp.id" value="wechat_id" />
        <property name="corp.secret" value="wechat_secret" />
        <property name="corp.media" value="wechat_media_id" />
        
        <exec executable="curl" outputproperty="corp.token.json" failifexecutionfails="false">
            <arg value="-s" />
            <arg value="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corp.id}&corpsecret=${corp.secret}"/>
        </exec>
        <!-- <echo message="${corp.token.json}" /> -->
        
        <script language="javascript">
            var jsonString = DJT.getProperty("corp.token.json");
            var json = eval ("(" + jsonString + ")");
            DJT.setProperty("corp.token",json.access_token);
        </script>
        <!-- <echo message="${corp.token}" /> -->
        
        <!-- 發(fā)送微信信息 -->
        <property name="build.digest" value="構(gòu)建完成${line.separator}${ant.project.name}-IOS [${build.config.name}]${line.separator}Tag:${git.tag} Commit:${git.commit}" />
        <property name="build.report" value="<p>構(gòu)建完成<br/>${ant.project.name}-IOS [${build.config.name}]<br/>${build.report.time}<br/>文件名稱<br/>${build.file.name}<br/>Tag:${git.tag} Commit:${git.commit}<br/>更新說明<br/>${build.type}<br/>${git.log}<br/>下載地址<br/><a href=\"${build.file.url}\" src=\"${build.file.url}\">${build.file.url}</a><br/><img src=\"${build.file.qrcode}\"/></p>" />
        
        <property name="build.report.json" value="{"touser":"","toparty":"${wechat.partyid}","totag":"","msgtype":"mpnews","agentid":${wechat.agentid},"mpnews":{"articles":[{"title":"Jenkins 構(gòu)建通知","thumb_media_id":"${corp.media}","author":"Jenkins","content_source_url":"","content":"${build.report}","digest":"${build.digest}","show_cover_pic":"0"}]},"safe":"0"}" />
        <!-- <echo message="${build.report.json}" /> -->
        
        <exec executable="curl" outputproperty="corp.message.json" failifexecutionfails="false">
            <arg value="-s" />
            <arg value="-l" />
            <arg value="-H" />
            <arg value="Content-type: application/json" />
            <arg value="-X" />
            <arg value="POST" />
            <arg value="-d" />
            <arg value="${build.report.json}" />
            <arg value="https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${corp.token}"/>
        </exec>
        
        <echo message="${corp.message.json}" />
        
    </target>
</project>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市甸怕,隨后出現(xiàn)的幾起案子甘穿,更是在濱河造成了極大的恐慌,老刑警劉巖梢杭,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件温兼,死亡現(xiàn)場離奇詭異,居然都是意外死亡武契,警方通過查閱死者的電腦和手機(jī)募判,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咒唆,“玉大人届垫,你說我怎么就攤上這事【牛” “怎么了敦腔?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恨溜。 經(jīng)常有香客問我符衔,道長找前,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任判族,我火速辦了婚禮躺盛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘形帮。我一直安慰自己槽惫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布辩撑。 她就那樣靜靜地躺著界斜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪合冀。 梳的紋絲不亂的頭發(fā)上各薇,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音君躺,去河邊找鬼峭判。 笑死,一個胖子當(dāng)著我的面吹牛棕叫,可吹牛的內(nèi)容都是我干的林螃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼俺泣,長吁一口氣:“原來是場噩夢啊……” “哼疗认!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伏钠,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤侮邀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贝润,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绊茧,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年打掘,在試婚紗的時候發(fā)現(xiàn)自己被綠了华畏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡尊蚁,死狀恐怖亡笑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情横朋,我是刑警寧澤仑乌,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響晰甚,放射性物質(zhì)發(fā)生泄漏衙传。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一厕九、第九天 我趴在偏房一處隱蔽的房頂上張望蓖捶。 院中可真熱鬧,春花似錦扁远、人聲如沸俊鱼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽并闲。三九已至,卻和暖如春谷羞,著一層夾襖步出監(jiān)牢的瞬間焙蚓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工洒宝, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萌京。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓雁歌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親知残。 傳聞我的和親對象是個殘疾皇子靠瞎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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