Jenkins构建iOS持续集成环境

2016-12-05 11:55:23
关于持续集成
1
2
持续集成是一种软件开发实践,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成
每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误

常用的持续集成工具

这里我们选择的是jenkins,好吧,安装就不多说了,不了解的同学请移步这里

前置说明
  • 实现 iOS 项目自动打包,需要有 Mac OSX 环境,Mac OSX 需要安装 Xcode ,并且系统中安装有 Xcode 的命令行工具
  • iOS 项目使用 CocoaPods 进行依赖管理,故 Mac OSX 需要安装 CocoaPods
  • 确保 Jenkins 服务器所在的机器上拥有对应的证书和 Profile 文件(用Xcode必选)
  • 如果需要静态扫描,可以安装Scan-build,用于静态扫描,请参考这里
  • 安装xtool,用于单元测试,具体参见这里(可选)
Jenkins的配置
  • 如果是git管理代码,需要安装git插件
    如果是svn管理代码,需要安装Subversion Plug-in插件
  • 安装Xcode插件(可选,如使用 Fir.im 的 CLI 可不需要安装 Xcode integration)
    选择系统管理->管理插件,在“可选插件”中选中“Xcode integration”安装
  • 安装签名证书管理插件(可选)
    iOS打包内测版时,需要发布证书及相关签名文件,因此这两个插件对于管理iOS证书非常方便。
    在系统管理->管理插件,在“可选插件”中选中“Credentials Plugin”和“Keychains and Provisioning Profiles Management”安装
  • 安装Post-Build脚本插件
    系统管理->管理插件,在“可选插件”中选中“Post-Build Script Plug-in”安装
  • E-mail的设置
    a. 首先要设置Jenkins的管理员邮箱,在Manage Jenkins->Configure System的“Jenkins Location”中设置“System Admin e-mail address”为需要的邮箱,也就是Jenkins发送邮件的发件人
    b. 接下来设置邮件SMTP的相关信息,在“E-mail Notification”区域中,点击“Advanced…”按钮,然后进行设置,首先填写SMTP服务器地址,选中“Use SMTP Authentication”的复选框,然后输入用户名和密码,最后在“Test configuration by sending test e-mail”中输入一个测试邮箱来测试邮件是否能发送成功。如果成功,会有相关提示
    备注:Jenkins管理员邮箱要与SMTP中设置的发送邮箱为同一个邮箱
  • Environment Injector Plugin
    系统管理->系统设置中可以进行全局配置,可以设置Xcode Builder(钥匙串设置)
    需要填写的内容:

    1
    2
    3
    4
    5
    Keychain Name:iPhone Distribution:*(dis证书常用名)
    Keychain path:${HOME}/Library/Keychains/login.keychain(dis证书路径)
    Keychain password:*
    Add to keychain search path after build:Yes
    Default keychain:iPhone Distribution:*
  • 安装FTP插件(可选)
    系统管理->管理插件,在“可选插件”中选中“Publish over FTP”安装

  • description setter plugin:用于在修改Build描述信息,在描述信息中增加显示QRCode(二维码)(可选)
  • Gradle plugin Android专用(可选)
  • Html publisher plugin 用于展示HTML报表(可选)
  • JUnit plugin 用于展示JUnit xml报表(可选)
自动化构建
  • 准备工作
    如果使用git,git仓库的ssh-key已经保存,描述文件和配置证书已经安装在机器上,证书已经设置为总是允许访问
    在Jenkins中,所有的任务都是以“item”为单位的。接下来我们就新建一个iOS的项目来开始自动化构建。点击“新建”,输入item的名称,选择“构建一个自由风格的软件项目”,然后点击“OK”。
    对于一个持续集成打包平台,每次打包都由4步组成:触发构建、拉取代码、执行构建、构建后处理。对应的,在每个Job中也对应了这几项的配置。
    其中执行构建可细化为:Xcode clean、静态扫描、单元测试XCTest、编译、打包等

  • 源码管理设置
    a) 如果是svn:选择svn填好路径,第一次配置会要求输入账户密码,以后就会自动记住了.
    Check-out Strategy最好选择每次update最新代码前都revert下,而不是“Use ‘svn update’ as much as possible” ,因为我使用的是cocoa pod管理的第三方,每次打包运行pod install会修改了工程配置文件,如果下次自动打包前不先revert再update的话会出现冲突
    b) 如果是git: 填写git的仓库地址,认证账户,需要构建的分支等,这里有两种情况:
    第一种如果Repository URL是HTTPS URL形式的,那么Credentials就要采用GitHub用户名密码的校验方式;而且,如果在GitHub中开启了2FA(two-factor authentication),那么还需要在GitHub中创建一个Personal access token,输入密码时将这个Personal access token作为密码进行输入
    第二种如果Repository URL是SSH URL形式的,那么就需要先在Jenkins所在的服务器上创建一个SSH秘钥对,并将公钥添加到GitHub的SSH keys中,然后在填写Credentials时,选择SSH Username with private key的校验方式,填入GitHub Username、SSH私钥、以及创建SSH秘钥对时设置的Passphrase

  • 触发条件设置
    设置build的触发条件,由于是做Daily Build,所以在“Build Triggers”中,选择“Build periodically”,然后在输入框中输入build的规则,这里假设我们的规则是每个工作日的下午6点25到30分之间进行build,所以在输入框中输入“H(25-30) 18 1-5”(点击输入框右边的问号,会有详细的规则编写说明)
    jenkin支持多种类型,常用的有
    a. 定期进行构建(Build periodically)
    b. 根据提交进行构建(Build when a change is pushed to GitHub)
    c. 定期检测代码更新,如有更新则进行构建(Poll SCM)

  • 构建环境设置
    如果使用Xcode,iOS打包需要签名文件和证书,所以这部分我们勾选“Keychains and Code Signing Identities”和“Mobile Provisioning Profiles”。
    这里我们又需要用到Jenkins的插件,在系统管理页面,选择“Keychains and Provisioning Profiles Management”。
    进入Keychains and Provisioning Profiles Management页面,点击“浏览”按钮,分别上传自己的keychain和证书。
    上传成功后,我们再为keychain指明签名文件的名称。点击“Add Code Signing Identity”
    我们的Adhoc证书和签名文件就已经在Jenkins中配置好了,接下来我们只需要在item设置中指定相关文件即可。
    回到我们新建的item,找到构建环境,接下来就可以配置xcode了

  • 编译设置
    a. 如果有静态扫描

    1
    scan-build  -o [OUTPUT_PATH] xcodebuild -configuration Debug  -sdk iphonesimulator

    静态扫描的结果为index.html,可以通过Html publisher plugin进行展示
    b. 如果是采用Xcode integration插件进行构建,配置会比较复杂,需要在Jenkins中导入开发证书,并填写多个配置项
    c. 如果是采用打包脚本进行构建的话,情况就会简单许多。只要在Jenkins所运行的计算机中安装好开发者证书,打包命令在Shell中能正常工作,那么在Jenkins中执行打包脚本也不会有什么问题
    我们既可以使用Jenkins自带的xcode插件,也可以自己编写脚本来完成。编写脚本时,可以直接使用Xcode的xcodebuild来写,也可以使用Facebook提供的xctool来做
    这里来介绍Xcode几个选项和附上shell脚本:

    • Target:就是我们在项目中建立的Target,如果不清楚有哪些Target的情况下可以到xcodeproj或xcworkspace的目录下运行如下命令
      xcodebuild -list 即可看到Targets,其中是所有可以用的target
    • Clean before build?:这个是在编译前是否clean一次,一般是选择YES。
    • Configuration: 对应的是xcodebuild命令里的-configuration的参数,可选项为[Debug、Release],一般都填Debug,这样就可以将打包后的ipa交付给测试人员测试
      Pack application and build .ipa?:这个是在结束是是否要产生对应的ipa文件,一般都是打上勾的
    • .ipa filename pattern:这个配置项是配置所产生ipa的文件名,其中有一些可用的全局变量
      其中${VERSION}和${SVN_REVISION}都是可以直接用的环境变量,如果想看还有哪些环境变量而已用可以点击在Execute shell下方有个 “See the list of available environment variables”进行查看
    • Output directory:这个配置了ipa的输出目录(默认Build output directory所在路径)
    • 如果使用脚本,仍然是点击“增加构建步骤”,选择“Execute Shell”
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      #<------------------------------------------------------->  
      # 删除旧源码目录->新建源码目录->从svn导出最新代码->清理旧文件->清除旧项目->打包->上传

      pwd

      # 名称配置
      checkout_name="checkout"
      project_name="Air"
      # 配置项目版本
      #targetProject_sdk="iphoneos8.0"
      targetProject_destination="generic/platform=iOS"
      configuration="Release"
      scheme="$project_name"
      workspace_name="${project_name}.xcworkspace"
      # 目录配置
      save_path="/Users/xiaoming/Desktop/uploadIPA"
      archive_path="$save_path/${project_name}.xcarchive"
      ipa_path="$save_path/${project_name}.ipa"
      log_path="$save_path/log.txt"
      # svn配置
      svn_path="http://192.168.1.110/svn/Air/trunk/iOS/Air"
      checkout_path="$save_path/$checkout_name"
      svn_name="xiaoming"
      svn_password="123456"
      # 配置签名证书、描述文件
      codeSignIdentity="iPhoneDeveloper: xiaoming zh (5AB779CDEF)"
      provision_UUID="06a7492b-083c-4313-d633-15ef685929g4"
      provisoning_profile="AirDevelopProfile"
      # 配置蒲公英
      upload_path="$save_path/${project_name}.ipa"
      pgy_userKey="a512b58c56285d23456e011fgh706509"
      pgy_apiKey="ab9c240d2efg9hi17j9642k3l5mnop5q"

      echo "正在删除旧源码"
      # 删除旧源码目录
      [ -e $checkout_path ]&&rm -rf "$checkout_path" >> $log_path

      echo "正在创建新的源码目录"
      # 新建源码目录
      cd "$save_path">> $log_path
      pwd
      mkdir "$checkout_name" >> $log_path

      echo "正在从svn下载最新的源码"
      # 从svn导出最新代码
      svn checkout "$svn_path" "$checkout_path" --username"$svn_name" --password "$svn_password" >> $log_path

      echo "正在删除旧文件"
      # 删除旧文件
      [ -e $log_path ]&&rm -rf "$log_path" >> $log_path
      [ -e $archive_path ]&&rm -rf "$archive_path" >> $log_path
      [ -e $ipa_path ]&&rm -rf "$ipa_path" >> $log_path

      echo "正在清除构建项目缓存"
      # 重要,执行xcodebuild命令时,必须进入项目目录
      cd "$checkout_path" >> $log_path
      pwd
      # 清理构建目录
      xcodebuild clean-configuration "$configuration" -alltargets >> $log_path

      echo "正在打包"
      # 归档(其他参数不指定的话,默认用的是.xcworkspace或.xcodeproj文件里的配置)
      xcodebuild archive -workspace"$workspace_name" -scheme "$scheme" -destination"$targetProject_destination" -configuration"$configuration" -archivePath "$archive_path"CODE_SIGN_IDENTITY="$codeSignIdentity" PROVISIONING_PROFILE="$provision_UUID">> $log_path

      echo "正在导出ipa包"
      # 导出IPA
      xcodebuild -exportArchive-exportFormat IPA -archivePath "$archive_path" -exportPath"$ipa_path" -exportProvisioningProfile"$provisoning_profile" >> $log_path

      echo "正在上传ipa到蒲公英"
      # 上传IPA到蒲公英
      curl -F"file=@$upload_path" -F "uKey=$pgy_userKey" -F "_api_key=$pgy_apiKey"https://www.pgyer.com/apiv1/app/upload
      #<------------------------------------------------------->
  • 编译后行为设置
    工程成功编译以后,我们可以设置编译出来的ipa文件(甚至可以直接是ota文件),将其与本次build的相关结果放到一起,提供下载。也可以在build失败时,发送邮件提醒。设置如下:点击“Add post-build action”选择“Archive the artifacts”,在输入框中输入“build/*.ipa”,就可以将编译打包后的ipa文件集成。点击“Add post-build action”选择“E-mail Notification”,在输入框中输入编译失败后邮件的通知者邮箱,如有多个,以空白字符分隔
    当iOS应用打包好后,我们还想发给其他相关人员安装,包括公司内部的,外网的,都需要。这时我们还需配置OTA服务和内网FTP
    外网安装App我们需要用到现在市面上比较流行的免费平台,蒲公英 在蒲公英官网设置相关信息后,我们可以写一个简单的脚本,来实现App打包后,上传到蒲公英和公司内网以及邮件提醒相关人员这一系列操作
    如果用Jenkins的插件配置FTP信息,进入系统管理页面,选择系统设置,找到“Publish over FTP”,填写信息后回到任务配置页面,点击“增加构建后操作步骤”,然后选择“Send build artifacts over FTP”,在填写信息后我们再点击“增加构建后操作步骤”,选择“Execute a set of scripts,执行相关脚本即可

  • 单元测试
    a. 如果使用XCTool

    1
    2
    XCTOOL_PATH  -workspace [YourProject's workspace] -scheme [YourSheme] -configuration Debug  -sdk iphonesimulator -launch-timeout 500 -reporter junit: [path/output.xml] test
    注意我们输出报表的格式为JUnit xml;

    b. 如果选择xcode自带的XCTest框架(Xcode5之前叫做OCUnit)。创建单元测试Job和自动化构建的Job过程一样,只在触发构建规则,build的脚本和编译后的规则有些不同
    单元测试的触发规则应该在git仓库的每次有新提交时就触发执行,所以在”Build Triggers”中,选择“Poll SCM”,在规则中写入“H/10 ”,意思是每十分钟轮询一次远程仓库,如果有新的提交,则开始构建。可以根据自己需求来设置轮询的时间间隔
    接下来是在build中输入单元测试脚本。这里需要有一些准备
    首先,由于Jenkins只接收Junit的单元测试报告,这里要安装一个将脚本执行结果的ocunit格式的测试报告转化为JUnit报告格式的脚本,该项目名叫OCUnit2JUnit
    需要在当前项目工程中,将项目schemes共享,并上传到远程仓库。在工程中选择“Manage Schemes”在弹出的菜单中勾选“Shared”,然后在git中将相应的shared shceme添加到版本控制中并上传到远程仓库
    Build”配置中,依然选择“Execute shell”,shell的内容如下:

    1
    xcodebuild test -scheme testCI -sdk iphonesimulator7.0 -destination OS=7.0,name="iPhone Retina (4-inch)" -configuration Debug  2>&1 | ocunit2junit

    这里的单元测试是在模拟器中进行,如果测试服务器连接着iOS设备,也可以设置在iOS设备中进行,只需修改上述shell的参数即可。
    最后是编译后行为的设置,这里要将测试报告加入。点击“Add post-build action”选择“Publish JUnit test result report”,输入内容test-reports/*.xml保存设置
    接下来在单元测试的Job中,点击“Build Now”来测试一下Job的配置,如果配置正确,则会看到模拟器启动,然后运行了一下程序,之后在build的结果里,可以看到相应的测试报告

可能遇到的坑
  • 使用jenkins server进行持续集成时会遇到模拟器无法启动的问题
    使用jenkins server进行持续集成时会遇到模拟器无法启动的问题,因此需要将jenkins模式从LaunchDaemon移到LaunchAgent。具体参见这里
    另外,将CreateSession从org.jenkins-ci.plist中移除,参考这里

ref
Jenkins 打包生成 ipa
Jenkins构建iOS持续集成环境
一步一步构建iOS持续集成:Jenkins+GitLab+蒲公英+FTP
使用Jenkins搭建iOS/Android持续集成打包平台


您的鼓励是我写作最大的动力

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。