Jenkins搭建iOS持续集成打包平台

准备工作

Jenkins的安装

Jenkins依赖于Java环境,首先需安装和配置Java环境(PS:在下载的时候注意选择JDK,而非JRE)

1.使用brew安装:

brew install jenkins

如果报错 brew: command not found,这是由于当前环境没有安装homebrew

安装homebrew ,代码执行:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装成功后,请重新安装brew install jenkins

安装成功后,浏览器会自动打开Jenkins网页服务,如果没有打开,请在浏览器输入 http://localhost:8080/。如果不能打开 http://localhost:8080/ ,可能是Jenkins服务未开启:

开启Jenkins :

sudo launchctl load /Library/LaunchDaemons/org.jenkins-ci.plist

停用Jenkins :

sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist

如果报错:/Library/LaunchDaemons/org.jenkins-ci.plist: No such file or directory

可以在命令行中开启服务:

jenkins

此时Jenkins会在命令行里打印相关的运行日志,再在浏览器里输入: http://localhost:8080/ 这种情况的话,如果关闭执行 jenkins命令的命令行窗口,Jenkins也会停止服务

Jenkins的配置

点击系统管理->管理插件,在插件管理里面下载几个比较有用的插件,当然可以根据自己的需求增减

1.插件的安装

Xcode integration(用来配置打包参数的,需配置的参数和步骤较多,改用脚本打包)
Keychains and Provisioning Profiles Management(用来配置钥匙串参数的,配合Xcode integration一同使用)
GitLab Plugin(用来关联gitlab仓库)
Gitlab Hook Plugin(用来配置hookurl,使gitlab在获得push 等事件的时候能唤起Jenkins)
fir-plugin(用来上传到fir的插件,易报错,已改用脚本上传)
Email Extension Plugin(用来在构建后发送邮件给测试的,smtp配置易出错,已改用脚步发送)
Changelog Environment Plugin(用来获取commits日志的,获取插件请访问 https://twiceyuan.com/2017/02/21/jenkins-changelog/)
BearyChat Plugin(用来在构建后发送通知给bearchat的,bearchat中选择Jenkins机器人即可接受消息)

2.插件的使用

gitlab plugin

Markdown
  • 仓库地址

  • 验证方式(用户名密码或ssh)

  • 分支(想要构建的分支名称,图中传了一个自定义的参数)

gitlab hook

Markdown
  • 在gitlab中设置-webhook中加入该URL,Jenkins即可在获取push/merge事件后开始构建

changelog plugin

  • 勾选该选项后,即可获取${SCM_CHANGELOG} commit日志参数,传入脚步

bearchat plugin

  • 勾选该选项后,即可在构建完成后通知给bearchat中的Jenkins机器人

项目设置

在Jenkins中,构建项目以Job的形式存在,因此需要针对每个项目创建一个Job。有时候,一个项目中可能有多个分支同时在进行开发,为了分别进行构建,也可以针对每个分支创建一个Job。
创建Job的方式有多种,本次只需要创建Freestyle project类型的即可。
Main page -> New Item -> Freestyle project

对于一个持续集成打包平台,每次打包都由4步组成:触发构建、拉取代码、执行构建、构建后处理。对应的,在每个Job中也对应了这几项的配置。

1.Jenkins主页,点击左上角的新建按钮开始新建一个Item

2.输入Item的名称,选择构建一个自由风格的软件项目

3.这里可以设置包的保留天数还有最大个数

4.配置参数化内容,如图,可以用${name}在shell命令中取出对应的值

5.在源码管理里面选择git,然后配置git信息

要对项目进行构建,配置项目的代码仓库是必不可少的。由于当前我们的项目托管在Gitlab私有仓库中,因此在此需要对Git进行配置。
【Source Code Management】配置栏目下,如果之前Gitlab plugin安装成功,则会出现Git选项。

配置Git代码仓库时,有三项是必须配置的:仓库URL地址(Repository URL)、仓库权限校验方式(Credentials),以及当前Job需要构建的代码分支(Branches to build)。
在配置Repository URL时,选择HTTPS URL或SSH URL均可。不过需要注意的是,Credentials要和Repository URL对应,也就是说:
如果Repository URL是HTTPS URL形式的,那么Credentials就要采用Gitlab用户名密码的校验方式;如果Repository URL是SSH URL形式的,那么就需要先在Jenkins所在的服务器上创建一个SSH秘钥对,并将公钥添加到GitHub的SSH keys中,然后在填写Credentials时,选择SSH Username with private key的校验方式,填入Gitlab Username、SSH私钥、以及创建SSH秘钥对时设置的Passphrase。

在配置Branches to build时,可以采用多种形式,包括分支名称(branchName)、tagName、commitId等。其中分支名称的形式用的最多,例如,若是构建master分支,则填写master,若是构建develop分支,则填写develop。
除了以上关于Git的必填配置项,有时根据项目的实际情况,可能还需要对Jenkins的默认配置项进行修改。比较常见的一种情况就是对clone的配置进行修改。

在Jenkins的默认配置中,clone代码时会拉取所有历史版本的代码,而且默认的超时时限只有10分钟。这就造成在某些项目中,由于代码量本身就比较大,历史版本也比较多,再加上网络环境不是特别好,Jenkins根本没法在10分钟之内拉取完所有代码,超时后任务就会被自动终止了(错误状态码143)。
这种问题的解决方式也很简单,无非就是两种思路,要么少拉取点代码(不获取历史版本),要么提高超时时限。对应的配置在Advanced clone behaviours中:

Shallow clone:勾选后不获取历史版本;
Timeout (in minutes) for clone and fetch operation:配置后覆盖默认的超时时限。

Markdown

6.配置构建触发器

代码仓库配置好了,意味着Jenkins具有了访问GitHub代码仓库的权限,可以成功地拉取代码。

那Jenkins什么时候执行构建呢?

这就需要配置构建触发策略,即构建触发器,配置项位于【Build Triggers】栏目。

触发器支持多种类型,常用的有:

  • 定期进行构建(Build periodically)
  • 根据提交进行构建(Build when a change is pushed to GitHub)
  • 定期检测代码更新,如有更新则进行构建(Poll SCM)

构建触发器的选择为复合选项,若选择多种类型,则任一类型满足构建条件时就会执行构建工作。如果所有类型都不选择,则该Jenkins Job不执行自动构建,但可通过手动点击【Build Now】触发构建。

关于定时器(Schedule)的格式,简述如下:

分钟(0-59) 小时(0-23) 日期(1-31) 月(1-12) 周几(0-7,0和7都是周日)


7.配置构建方式

常用的构建方式是根据构建对象的具体类型,安装对应的插件,然后采用相应的构建方式。例如,若是构建iOS应用,安装Xcode integration插件之后,就可以选择Xcode,然后选择Xcode进行构建。

该种方式的优势是操作简单,UI可视化,在场景不复杂的情况下可以快速满足需求。不过缺点就是依赖于插件已有的功能,如果场景较复杂时可能单个插件还无法满足需求,需要再安装其它插件。而且,有些插件可能还存在一些问题,例如对某些操作系统版本或XCode版本兼容不佳,出现问题时我们就会比较被动。
我个人更倾向于另外一种方式,就是自己编写打包脚本,在脚本中自定义实现所有的构建功能,然后在Execute Shell中执行。这种方式的灵活度更高,各种场景的构建需求都能满足,出现问题后也能自行快速修复。

另外,对于iOS应用的构建,还有一个需要额外关注的点,就是开发者证书的配置。
如果是采用Xcode integration插件进行构建,配置会比较复杂,需要在Jenkins中导入开发证书,并填写多个配置项。不过,如果是采用打包脚本进行构建的话,情况就会简单许多。只要在Jenkins所运行的计算机中安装好开发者证书,打包命令在Shell中能正常工作,那么在Jenkins中执行打包脚本也不会有什么问题。

8.至此,我们的Jenkins设置就全部完成了

点击构建,就会开始构建项目了,构建一次,各个颜色代表的意义如下:


如果构建失败了,可以去查看Console Output可以查看log日志:


关于iOS的构建

对iOS源码进行构建,目标是要生成.ipa文件,即iOS应用安装包。
当前,构建方式主要包括两种:

xcodeuild:

  • 源码 -> .archive文件 -> .ipa文件

xcrun:(xcode8后已被淘汰)

  • 源码 -> .app文件 -> .ipa文件

这两种方式的主要差异是生成的中间产物不同,对应的,两种构建方式采用的命令也不同,我们只看xcodebuild这种.

源码 -> .archive

# build archive file from source code
xcodebuild archive -workspace ${project_name}.xcworkspace \
                   -scheme ${scheme_name} \
                   -configuration ${build_configuration} \
                   -archivePath ${export_archive_path}

archive:对编译结果进行归档,会生成一个.xcarchive的文件,位于-archivePath指定的目录中。

需要注意的是,对模拟器类型的sdk无法使用archive命令.

.archive -> .ipa

# export ipa file from .archive
xcodebuild  -exportArchive \
            -archivePath ${export_archive_path} \
            -exportPath ${export_ipa_path} \
            -exportOptionsPlist ${ExportOptionsPlistPath}

参数说明

xcodebuild参数:

  • -workspace:需要打包的workspace,后面接的文件一定要是.xcworkspace结尾的;
  • -scheme:需要打包的Scheme,一般与${project_name}相同;
  • -configuration:需要打包的配置文件,我们一般在项目中添加多个配置,适合不同的环境,Release/Debug;
  • -archivePath:.xcarchive文件的路径;
  • -exportPath:导出文件(.ipa)的路径;
  • -exportOptionsPlist:根据configuration对应的plist文件;

补充说明

1、获取Targets、Schemes、Configurations参数

在填写target/workspace/scheme/configuration等参数时,如果不知道该怎么填写,可以在项目根目录下执行xcodebuild -list命令,它会列出当前项目的所有可选参数。

Information about project "Store":
    Targets:
        Store
        StoreCI
    Build Configurations:
        Debug
        Release
    If no build configuration is specified and -scheme is not passed then "Release" is used.
    Schemes:
        Store
        StoreCI

2.清除缓存文件

在每次build之后,工程目录下会遗留一些缓存文件,以便下次build时减少编译时间。然而,若因为工程配置错误等问题造成编译失败后,下次再编译时就可能会受到缓存的影响。
因此,在持续集成构建脚本中,比较好的做法是在每次build之前都清理一下上一次编译遗留的缓存文件。

xcodebuild clean -workspace ${project_name}.xcworkspace \
                 -scheme ${scheme_name} \
                 -configuration ${build_configuration}

3.处理Cocoapod依赖库

若项目是采用Cocoapod管理项目依赖,每次拉取最新代码后直接编译可能会报错。这往往是因为其他同事更新了依赖库(新增了第三方库或升级了某些库),而本地还采用之前的第三方库进行编译,从而会出现依赖库缺失或版本不匹配等问题。
应对的做法是,在每次build之前都更新一下Cocoapod。

# Update pod repository
pod repo update
# Install pod dependencies
pod install
# Install pod dependencies not update spec
pod install --verbose --no-repo-update

4.上传FIR

fir publish ./xxx.ipa -T xxxxxx

5.提交AppStore(未完成)

/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool --validate-app -f ./xxx.ipa -u xxx -p xxx -t ios --output-format xml
/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool --upload-app -f ./xxx.ipa -u xxx -p xxx -t ios --output-format xml

6.发邮件

python sendEmail.py "日志" "用户邮箱"

遇到的问题

Q1:Jenkins Xcode 证书设置错误 Code Sign error: No matching codesigning identity found: No codesigning identities

A1:Jenkins 集成Xcode 项目的时候在证书上遇到了问题。实际上如果在本地的话。只要Xcode工程里选择了项目就不需要重新设置证书了。jenkins会自动找到这个证书,只要在build setting 里设置的是正常的。并且在xcode 里能正常编译。

Check dependencies
Code Sign error: No codesigning identities found: No codesigning identities (i.e. certificate and private key pairs) that match the provisioning profile specified in your build settings (“qingyunDeveloper”) were found.

如果遇到类似的错误 解决办法:
因为jenkins运行在Mac的守护进程模式,只是认为它是一个不同的用户,所以不会有机会获得钥匙串或提供个人资料作为您登录使用您的凭据,而我的证书是装在“登录”下的,这会导致代码签名有问题
解决办法是首先打开keychain keys 找到apple 的开发者证书。然后复制。 再选择左边的系统(system)把刚复制的证书放进去。

如果这个还没有解决。

接下来第二步:

找到你用户下的Provisioning Profiles 文件。目录为 /Users/xxx/Library/MobileDevice/Provisioning Profiles xxx表示你自己的用户名
把这里面所有的证书复制到/Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profile 这个文件。

参考资料:
http://code-dojo.blogspot.co.uk/2012/09/fix-ios-code-signing-issue-when-using.html

Q2:打包iOS有pod的项目,shell执行pod install时,提示pod command not found

A2:需要在shell第一行加上 #bin/bash -l 就行了,所以最终是这样:

#!/bin/bash -l
export LANG=en_US.UTF-8
pod install --verbose --no-repo-update

Q3:有pod的项目提示schema找不到

A3:需要用xcode打开一次.xcworkspace就好了,或者在shell pod install之后下加上 open x.xcworkspace

Q4:jenkins 命令 command not found

A4:由于jenkins是用jenkins用户启动的,所以很多环境变量就没有,导致很多命令起不来,所以需要把jenkins改为自己常用的用户来启动。默认jenkins的目录是在/User/Share/Jenkins下的。这是安装.pkg文件成功后自动创建了jenkins用户,下面我们开始切换到常用用户下。

1)我们先把jenkins停掉,执行:
sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist

然后刷新浏览器,发现jenkins页面打不开了,说明成功停掉了

2)到Finder->application下找到jenkins/jenkins.war,双击启动。然后刷新浏览器,发现又重新进入了jenkins的配置过程,正常配置完,返现jenkins根目录变了,变成/User/当前用户/.jenkins/

Q5:jenkins打包的时候需要钥匙串密码

A5:这个密码是本地登陆密码,如果不行,就得用jenkins用户的密码,直接重置jenkins密码。在命令行执行
sudo passwd jenkins
可能要求先输入当前用户密码,然后就提示Changing password for jenkins.设置新密码就行了.

Q6:在使用jenkins打包时 出现xcodebuild: error: The workspace named "XXXX" does not contain a scheme named “XXX"

A6:首先在项目根目录中使用 xcodebuild -list 获得schema名称
然后在该schema中勾选shared选项

Markdown

Q7:jenkins使用shell命令打IOS包报错:user interaction is not allowed

A7:在编译命令前加入以下命令:

security unlock-keychain -p "123456" ~/Library/Keychains/login.keychain

或者

echo "123456" | security unlock-keychain  ~/Library/Keychains/login.keychain

[要把钥匙串解锁,并配置访问钥匙串的密码即可]

Q8:在自动化部署的时候,gitlab中的webhook无法访问jenkins,报错execution expired

A8:暂时没找到办法解决,可能是架设gitlab的局域网网段和jenkins的网段不一致

Q9:不能打包adHuc/appstore包的问题

A9:下载描述文件(需要把extension的描述文件一起下下来) p12文件一起安装

Q10:在打包时出现多语言包损坏的问题

A10:不要用cocoapods的beta版

Q11:打包脚本报错teamid不匹配

A11:Xcode近年来致力于自动管理开发证书,每个大版本都会有修改(有方便的地方也有坑的地方)。到了Xcode8,在target的General设置面板中直接新增了“Signing”,看得出来,Automatically manage signing选项是苹果推荐的签名方式。如果恰巧不幸,你的证书里的App ID与Xcode中的bundle ID不符,就应该关闭该选项,手动为某个configuration指定打包证书。不要忘记在脚本中-exportOptionsPlist指定的plist中配置对应的teamID。

总结

  • 自动打包是iOS开发中的一项基础工作,作为自动化工作的一小部分往往被应用于CI系统。持续集成的内容很多,还有自动化测试、代码静态检查、持续交付等内容。这篇文章是概括了其中一些方面,我们要做的还有很多。

接下来要做什么

  • OCLint -- 做为一个静态代码分析工具,我们引入 OCLint 的目的主要是为了提高我们的代码质量。通常我们提高代码质量的方式是通过 CodeReview,但是这个过程耗费的人工和时间往往较大,所以我们想通过 OCLint 的一些规则,让机器帮我们完成一部分代码质量的检测,从而提高我们的工作效率。
    参考链接:

    iOS 工程自动化 - OCLint

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容