Jenkins实现持续集成-使用Ant脚本构建ios项目

原文 : tomczhen的博客

在Jenkins平台上使用Ant脚本构建iOS项目, 可以做到自动构建打包, 上传蒲公英, 构建完成发送微信通知 等功能. 发布开发者进行测试分发,只需要点一下即可完成一系列操作.

Jenkins构建面板
Jenkins构建面板

安装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
    

配置运行环境

在Jenkins的系统设置页面设置ANT_HOME路径,并在全局属性中添加以下键值。

  • LANGzh_CN.UTF-8
  • PATH系统PATH输出值

编写Ant脚本

  • 官方手册,可以查看可用的任务或命令以及具体的参数,实例。

添加Jenkins任务

  • 新建任务类型”构建一个自由风格的软件项目”,选择源代码管理的方式。在”增加构建步骤”中选择Invoke Ant,打开高级选项,配置好Targets,在Build File中输入配置好的build.xml文件路径。

  • 注意:可以在任务中只配置源代码管理,执行任务测试获取代码是否正常,然后在服务器上的命令行界面中使用ant命令调用项目中的build.xml文件进行编译测试。


脚本实例

脚本说明:

  • ant脚本本质上还是通过调用xctool或xcodebuild命令进行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>
    
    <!-- 获取当前 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" />
        <!-- 构建开发环境配置信息 -->
        <property name="build.type" value="Dev" />
        <property name="build.config.name" value="开发环境" />
        <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}" />
        <!-- 接口服务器地址 -->
        <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" />
        <!-- 构建测试环境配置信息 -->
        <property name="build.type" value="Beta" />
        <property name="build.config.name" value="测试环境" />
        <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}" />
        <!-- 接口服务器地址 -->
        <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" />
        <!-- 构建生产环境配置信息 -->
        <property name="build.type" value="Release" />
        <property name="build.config.name" value="生产环境-蒲公英" />
        <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}" />
        <!-- 接口服务器地址 -->
        <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" />

        <!-- 构建正式环境配置信息 -->
        <property name="build.type" value="AppStore" />
        <property name="build.config.name" value="正式环境-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}" />
        <!-- 接口服务器地址 -->
        <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">
    <!-- 配置接口服务器地址 -->
        <!-- 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">
    <!-- 发布到蒲公英平台 -->
        <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 微信企业号应用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}" /> -->
        
        <!-- 发送微信信息 -->
        <property name="build.digest" value="构建完成${line.separator}${ant.project.name}-IOS [${build.config.name}]${line.separator}Tag:${git.tag} Commit:${git.commit}" />
        <property name="build.report" value="<p>构建完成<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 构建通知","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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容