Jenkins 持续集成iOS、Android项目


目录

一、什么是持续集成?

二、我们为什么要用持续集成?

三、安装环境配置

四、iOS 项目配置

五、Android 项目配置

六、写在最后


一、什么是持续集成?

持续集成指的是频繁主动的将代码集合并到代码仓库当中,方便产品保持高质量的快速迭代。持续集成的好处主要有:

1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。

2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

在一次的集成都必须通过自动化的构建(包括编译、打包、发布、自动化测试等)来验证,从而尽早的发现集成当中的错误。

与持续集成相关的,还有两个概念,分别是持续交付持续部署

持续交付(Continuous delivery)指的是,频繁地将软件的新版本,交付给质量团队或者用户,以供测试。如果测试通过,代码就进入生产阶段。

持续部署(continuous deployment)是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。

二、我们为什么要用持续集成?

团队中目前最大的痛点就是开发的时间被碎片化。随着产品种类越来越多,也冒出来了各种各样的测试Case,特定的环境、特定的跳转、特定的需求,开发同学在打包上花费的时间也是越来越多。任务经常性的被打断,还有可能分心导致代码质量的下降等等。为了解决这种问题,我们打算为内部同学提供一套简单自动的开发者环境,引入了Jenkins 持续集成。前期主要的目的就是提供一套可以动态配置常用参数、自动构建安装包、自动分发下载的平台。后期会接入QA 相关的自动化测试、静态代码检测,安装包内也会加大对开发者功能的支持。

三、安装环境配置

由于要支持iOS 与Android 两种不同系统,最终选择Mac 系统当做服务器系统,版本10.12.4。Jenkins 版本2.46.2,这里就不赘述Jenkins 的安装方法与登录方法了。

以下列举了目前使用到的Jenkins 插件和版本号,吐槽一下,配置过程中发现有些以前教程里的插件现在根本搜不到,无奈只能用其他方式解决:

Android Emulator Plugin                    2.15

Android Lint Plugin                             2.4

build-name-setter                               1.6.5

description setter plugin                     1.10

GitHub Organization Folder Plugin     1.6

Gitlab Hook Plugin                              1.4.2

GitLab Plugin                                      1.4.5

Global Post Script Plugin                    1.1.3

Gradle Plugin                                      1.26

Keychains and Provisioning Profiles Management          1.0.0

Publish Over FTP                                1.12

Xcode integration                                 1.4.11

四、iOS 项目配置

这里最主要使用的两个Jenkins 插件是Xcode integration和Keychains and Provisioning Profiles Management。

1. 配置Keychains and Provisioning Profiles Management

安装好本插件后,进入Jenkins--系统管理会多出来一个“Keychains and Provisioning Profiles Management” 选项。

首先进行上传login.keychain 文件,查找地址是

/Users/用户名/Library/keychains/login.keychain

接下来指定Code Signing Identity,可以从Xcode 里的Signing 选项中或者通过命令行查看login.keychain 内包含的:

security find-identity -p codesigning 地址/login.keychain

比如这里我选择了正式打包时使用的,

iPhone Distribution: XXXXX (Beijing) Network Technology Co., Ltd (3GEKQMXXXX)

配置Provisioning Profiles 目录,这里最好将整个开发机上的钥匙串和配置文件目录都拷贝到Jenkins/Library 目录下,

/User/用户/Library/Keychains

/User/用户/Library/MobileDevice/Provisioning Profiles

所以配置Jenkins 的Provisioning Profiles Directory Path为:

/Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profiles

3. 配置iOS Jobs

1)新建--构建一个自由风格的软件项目

2)选中“丢弃旧的构建”,普遍选择保留3天,30个构建个数。

3)选择“参数化构建过程”,可根据业务配置动态参数,使用${NAME} 可得到选择的值。

4)源码管理选择Git,填写Repository URL 代码仓库地址,Credentials 新建这里我选择了username 和password 的方式进行访问。

这里需要注意下系统Git 的版本号,如果Git 版本低于1.8 可能会一直出现401 权限问题,需要升级Git 版本到1.8 以上。

5)构建触发器

可以配置定时任务、触发式任务等,触发式任务可定期检测代码仓库是否有更新,并自动执行构建操作。配置规则Jenkins 的帮助写的很详细,这里就简单列一下:

第一个参数代表的是分钟 minute,取值 0~59;

第二个参数代表的是小时 hour,取值 0~23;

第三个参数代表的是天 day,取值 1~31;

第四个参数代表的是月 month,取值 1~12;

最后一个参数代表的是星期 week,取值 0~7,0 和 7 都是表示星期天。

如H/15 * * * * 表示的就是每15分钟检查一次源码变化。

6)构建环境,选中Keychains and Code Signing Identities,Keychain 行选择之前配置的login.keychain 即可。

弹出的选择框中可能无法选择Keychain,这个时候先选中Keychain and Code Singing Identities 选项,先保存一下当前配置,重新进入Jobs 配置页面就可以正常选择了。

7)构建

由于要执行pod 和动态参数改变,构建前先执行我们自己的脚本文件。增加构建步骤,选择Execute shell,注意位置要放在“构建”模块的第一位。

#bin/bsah - l

export LANG=en_US.UTF-8

export LANGUAGE=en_US.UTF-8

export LC_ALL=en_US.UTF-8

cd $WORKSPACE/你的项目文件

/usr/local/bin/pod update --verbose --no-repo-update

#这里可以做一些动态参数的替换,例如:

oldParm="www.baidu.com"

newParm="www.sina.com"

parmSed="sed -i \"\" \"s/$oldParm/$newParm/g\" main.pch"

eval $parmSed

再次增加构建步骤,选择Xcode 插件。General build settings:

如果当前有Target ,就填写;我们项目因为用到了Today 插件,不止一个Target,就不填写。

选中Clean before build?,每次编译前Clean 一下。

选中Generate Archive?

通过Configuration 可以选择当前是Release 版本还是Debug 版本。

选中Pack application and build .ipa?,指定生成ipa 包,.ipa filename pattern 包名规则自定义,这里为app_${VERSION}_${BUILD_DATE}。

Output directory 目录为${WORKSPACE}/build/ ,指定打包后的ipa 文件输出目录。

Code signing & OS X keychain options:

选中sign IPA at build time,选中unlock keychain,Keychain Path 填写${KEYCHAIN_PATH},钥匙串密码这里为系统用户登录密码。

Advanced Xcode build options:

配置Xcode Schema File,可以通过Xcode 的Manage Schemas... 查看现有的Schema。

Custom xcodebuild arguments 配置为PROVISIONING_PROFILE=${项目Target_PROVISIONING_PROFILE}。

Xcode Workspace File 配置为${HOME}/Home/workspace/AppForiOS/xxxx,这里指向的是项目的xcworkspace文件,但是在构建过程中Jenkins 会自动追加.xcworkspace 后缀名,填写时需要注意。注意:这个参数配置了,下面的Xcode Project File 参数配置就无效了。

Xcode Project Directory 配置为包含了xcodeproj 文件的目录,只需要单独一个目录名就可以,这里是相对路径从$workspace 开始。

Xcode Project File 如果要配置的话指向xcodeproj 文件即可。

Build output directory 输出指向${WORKSPACE}/build/,这里的输出不止包括.ipa 安装包。

8)构建后操作

构建后的流程是上传至蒲公英后在Jenkins 的构建历史中显示下载地址、二维码等,或者可以FTP 上传到内部服务器、发送邮件等,根据需求选择。

增加构建后操作步骤选择Post build task,勾选Run script only if all previous steps were successful,脚本执行蒲公英上传功能:

#蒲公英上的User Key

uKey="3f9526ca10b8d43f3fa881b98xxxxx"

#蒲公英上的API Key

apiKey="95f337f90fe42dad8bb84xxxxxxx"

cd build

name="$(find . -maxdepth 1 -name '*.ipa')"

#要上传的ipa文件路径

IPA_PATH="${WORKSPACE}/build/$name"

#执行上传至蒲公英的命令

curl -F "file=@$IPA_PATH" -F "uKey=$uKey" -F "_api_key=$apiKey" https://qiniu-storage.pgyer.com/apiv1/app/upload

再次增加构建后操作步骤,选择Set build description(注意上下顺序),

Regular expression 配置"appQRCodeURL":"(.*)",这里可以根据正则从日志中匹配到想获得的值,在Description 里使用\1、\2等展示正则匹配的值,这里就自由发挥了。如果要以HTML 的方式(显示图片等)展示描述信息,需要将Jenkins--系统管理--Configure Global Security--Markup Formatter 选项改为“Safe HTML”。

4. 配置中的问题

由于Jenkins 是以“Jenkins”用户的身份进行操作,构建过程中会出现各种的权限问题,我都是比较暴力的直接修改文件的权限进行解决。

构建出现错误:xcrun: error: unable to find utility "PackageApplication", not a developer tool or in PATH。

解决方法:发现Xcode 8.3 以后已经废弃了PackageApplication,所以解决方式就是从旧版上复制一份来使用。

https://pan.baidu.com/s/1kVqP8xx 下载PackageApplication

拷贝至/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ 目录下

执行命令 sudo xcode-select -switch/Applications/Xcode.app/Contents/Developer/

chmod +x/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication

执行pod 命令时出现ERROR: SSL verification error at depth 1: unable to get local issuer certificate (20)

解决方法:出现这个问题的原因主要是Ruby环境需要2.2版本以上,所以要更新Ruby环境。

1.安装rvm:$ curl -L get.rvm.io | bash -s stable

2.装载rvm:$ source ~/.rvm/scripts/rvm

3.安装2.3.0版本ruby:$ rvm install 2.3.0

4.将2.3.0设为默认:$ rvm use 2.3.0 --default

执行pod 时出现env: ruby_executable_hooks: No such file or directory

解决方法:尝试升级ruby、尝试安装executable_hooks、尝试修改命令脚本引用、尝试修改Jenkins 环境变量PATH。

这个卡的时间比较长,也是各种资料查。因为在服务器机器上执行pod,或者配置Path 后终端都可以正常使用,但是在执行Jenkins 构建的时候却一直失败。一开始以为是环境版本太低,能升级的都升了。后来还尝试安装了executable_hooks(命令sudo gem install --user-install executable-hooks)。尝试过去修改pod 命令里的引用,直接指到安装后的ruby 目录(引用!#/Users/Shared/Jenkins/.rvm/rubies/ruby-2.3.0/bin ruby_executable_hooks)或者修改GEM_PATH(/Users/Shared/Jenkins/.rvm/gems/ruby-2.3.0/environment)等等。最后看到有可能是Jenkins 执行Shell 脚本时的环境与服务器的系统环境不相同,尝试在Shell 脚本中输出一些变量值进行观察,因为对Shell 脚本不是很熟悉,最后去Jenkins--系统管理--系统设置--全局属性中,新增键值对PATH,值指向新版本ruby(/Users/Shared/Jenkins/.rvm/rubies/ruby-2.3.0/bin:$PATH)

五、Android 项目配置

配置好iOS 项目后,再进行Android 的配置就简单很多,这里主要是对Android 的开发环境进行配置,主要使用的Jenkins 插件是Gradle Plugin。

1. 环境配置

Jenkins--系统管理--系统设置

选中Environment variables,新增键值对ANDROID_HOME,指向Android 的SDK目录。

Jenkins--系统管理--Global Tool Configuration

Gradle 安装,随便起一个Gradle 配置名称,GRADLE_HOME 配置/Applications/Android Studio.app/Contents/gradle/gradle-x.x。

2. Jobs 配置

其他部分与iOS 配置类似,只需要增加构建步骤从Xcode 切换成Invoke Gradle script 插件,

选中Invoke Gradle,Gradle Version 选择刚才已经配置好的Gradle 名称。

Task 任务内填入clean build,表示执行clean 和build 两个操作。这里需要注意build 操作会将项目中build.gralde 配置文件中buildTypes 下所有的配置都执行一遍,第一次时没有注意就执行了build,结果10+个配置一共执行了45分钟。如果想打单独配置的包,填入assemblerelease 包,根据buildTypes 内的定义即可。

Root Build script,配置${workspace}/app/,这里指向的是项目使用的build.gradle 文件目录。如果build.gradle 文件就在项目根目录,则不用填写。

Post build task,增加APK 签名命令,jarsigner -verbose -keystore keystore地址 -signedjar 签名后的文件名 -digestalg SHA1 -sigalg MD5withRSA 未签名的安装包地址 keystore的alias -storepass keystore的密码

3. 配置中的问题

Jenkins构建Android 出现/Users/Shared/Jenkins/.android/analytics.settings(No such file or directory)

解决:创建.android 目录

SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable

解决:项目中缺少local.properties文件,主动创建,填写内容sdk.dir=“sdk目录”

Jenkins A problem occurred configuring project‘:app’. > The SDK directory‘/User/用户/Library/Android/sdk' does not exist

解决:将改目录的权限全部设置成了777

六、写在最后

目前只是实现了简单的基础功能,发现坑确实不少,但不得不说Jenkins 的日志真的很详细,定位问题很方便。以上就是Jenkins 的一些经验,特此记录总结一下。

参考链接

阮一峰 http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html

七、补充汇总

在新复制一个带有“Git Parameter”插件的项目后,点击“Build with Parameters” 时,选择分支的选择框内提示“null Please look at the log”。

解决:由于在Jenkins workspace 目录下没有任何相关的代码,只需要直接进行构建,拉取一次远程代码到本地,再次点击就会正常显示分支信息了。 


在配置iOS 静态代码扫描时,使用sonar-objective-c-plugin-0.5.0-SNAPSHOT.jar 插件进行扫描,该插件版本与sonarQube 系统版本相关,需要注意。目前遗留问题是OC 的扫描规则太少,扫描结果只能扫出坏味道,Bug 和漏洞都无法扫到。配置文件增加了如下配置也没有效果:

sonar.objectivec.oclint.report=oclint.xml

sonar.objectivec.oclint.reportPath=sonar-reports/oclint.xml

推荐阅读更多精彩内容