美文网首页运维
自动化打包实战 — Jenkins

自动化打包实战 — Jenkins

作者: 地上的 | 来源:发表于2018-11-12 15:49 被阅读294次

继上篇写过 自动化打包实现 — Fastlane 之后,感触良多,因为出文之后可以不断的激励自己去优化和改进脚本文件,所以这篇Jenkins也再计划之中。

历史

原型是Hudson,是一个Java语言编写的开源的持续集成工具。最开始是2004年Sun公司一个员工基于个人爱好做的好玩的一个项目,名叫Hudson,后来经过几年的发展越来越多团队开始使用它。
再后来Oracle收购了Sun,然后由于Oracle和负责Hudson的团队发生矛盾冲突,负责团队就copy一份Hudson出来,命名为Jenkins,开始了继续维持开放、灵活、快节奏的社区主导型模式。

Jenkins分布式构建架构

Jenkins使用主/从架构来管理分布式构建。
主节点:处理调度构建作业,把构建分发到从节点来进行实际执行,监视从节点,并且记录和发布构建产物。
从节点:按照被告知的工作,即主节点分派的构建作业。配置可分为如下三种情况:
配置一个项目总是在特定的从节点运行
在某个特定类型的从节点运行
让Jenkins挑选下一个可用的从节点
一旦从节点实例运行,它就通过TCP/IP连接主实例进行通信。

Jenkins主/从策略

Jenkins可用通过很多不同的方式设置分不只构建,取决于操作系统和网络架构。
构建作业运行在从节点的方式及怎么被管理对于终端用户来说都是透明的:构建结果和构建产物最后总是会在主服务器上。

Jenkins和Fastlane的对比

既然有了Fastlane去做自动化打包,为啥还要折腾Jenkins呢?
1、Fastlane不够自动化,为啥?因为无法自动在某个时间点去启动执行,也无法在有了新的提交时立马响应去执行编译,非得你手动去敲fastlane action
2、Fastlane严格来说只能算是持续集成(Continuous Integration,简称CI)的一个自动化打包部分或者模块。

持续集成

那么问题来了,什么才是完整的持续集成?

持续集成(英语:Continuous integration,缩写CI)是一种软件工程流程,是将所有软件工程师对于软件的工作副本持续集成到共享主线(mainline)的一种举措。该名称最早由葛来迪·布区(Grady Booch)在他的布区方法中提出,不过他并不支持在一天中进行数次集成。之后该举措成为极限编程(extreme programming)的一部分时,其中建议每天应集成超过一次,甚至达到数十次。在测试驱动开发(TDD)的作法中,通常还会搭配自动单元测试。持续集成的提出主要是为解决软件进行系统集成时面临的各项问题,极限编程称这些问题为集成地狱。

好了,以上都是维基的废话,简单来说,就是由3个模块组成:
1、一个可以自动构建的过程,包含自动编译、打包、分发、部署、测试等等。
2、一个代码仓库,SVN、Git等。
3、一个个持续集成的服务器,让你的自动构建的过程不停的运行。

那接下来就开撸Jenkins。

一种比较非常规的启动方式

使用Java Web Start方式。Java Web Start是一种绑定了Java JRE的技术,可以通过Web页面的URL在你的机器上启动Java应用程序。https://wiki.jenkins.io/display/JENKINS/Meet+Jenkins,虽然他这个也只是引导去下载Jenkins.war。(这种方式貌似被官方废弃了。。。)

Jenkins 安装和启动

安装这里一定要注意!!!一定注意!!!前方有巨坑!!!
有些文章说什么到官网下载war去安装,这种方法巨坑巨坑巨坑!!!(重要的事说三遍)
为什么?
因为这样的安装方式,会导致系统自动创建一个Jenkins账户(对的,就是类似于你的电脑用户,名叫Jenkins),然后你懂的,以后所有的操作就会有至少以下几个巨大的问题:
1、你原来本机里的证书、描述文件、钥匙串密码、公钥私钥等等等等,全部都特么在你原来自己那个用户里面啊,然后你就在配置Jenkins时会遇到各种问题。
2、你操作起来也非常不方便啊!因为你当前用户没有Jenkins那个用户的各种权限(文件读写、删除等),所以经常各种无权限的提示...

下面我们开始使用homebrew安装Jenkins。
打开终端,命令行:brew install jenkins(没安装homebrew的自行Google,如此神器不安装算是一大损失)。
等待安装完成(如若安装失败,可能是Java环境问题),即可启动Jenkins。在终端中输入jenkins回车即可。
随后在Safari浏览器中打开http://localhost:8080,首次启动会出现如下界面:


这里需要输入初始密码,初始密码是一个随机串,地址位于.jenkins/secrets/initialAdminPassword中(需打开隐藏文件夹 command + shift + .),打开此文件,复制密码到浏览器输入框中即可初始化解锁。
随后会出现一系列的初始化界面,顺着点就行了。

随后刷新一下页面,会让你注册管理员账户,注册登录完毕后,即完成了Jenkins的安装和启动。

Jenkins 配置

插件配置

先不着急创建项目,首先得进行各种插件的配置。
入口:系统管理 —> 插件管理
勾选需要的插件,然后点击download now and install after restart,以下是一些常规需要的插件:
1、Build Timeout:用来设置Jenkins自动打包过程中,超时多久会自动fail掉。
2、Xcode integration:供xcode可编译项目使用。
3、GIT plugin:连接Git使用。
4、GitLab Plugin
5、Gitlab Hook Plugin:以上两个用于读取你在Gitlab上面的项目分支,编译特定分支上的代码。
6、Keychains and Provisioning Profiles Management:配置钥匙串和证书、描述文件等。
配置完插件之后,如果系统没有自动restart,那就手动输入http://localhost:8080/restart进行重启。

Keychains 和 描述文件 配置

经过上面步骤重启止之后,点击系统管理,会多出一条:Keychains and Provisioning Profiles Management模块。


点击进入之后,开始选择Keychains和Provisioning Profiles上传。
1、上传Keychain:keychain本地目录在:/Users/xxxxxxx/Library/Keychains/login.keychain-db,对的,他是一个-db后缀的文件,很多老的Jenkins配置文章里没有更新这点,在macOS High Sierra后该文件变成了这样的后缀,直接上传会导致文件类型不一致上传失败,于是我们把它copy一份到桌面,然后直接修改文件名为:login.keychain,然后再上传就OK了。
2、上传描述文件
把你从develop.apple.com上面下载的描述文件一一上传,一般包含adhoc、development、AppStore三种类型都上传上去。类似于:
保存一下,即完成了keychain和描述文件的配置。
创建项目


创建一个新的项目,输入项目名称,类型选择构建一个自由风格的软件项目,点击ok之后会出现这个项目的配置界面,之后也可以从首页所有项目,点击进去,在Configure里面继续配置。
配置项目

1、General,我这里的描述就简单写了下,这里重要的部分是勾选☑️丢弃旧的构建,我这里设置的每个build保存5天,最多保存30个build包,防止无用的旧包占储空间。


2、Source Code Management

这里首先选择☑️Git,然后填上你的gitlab上面的地址,我这里选用的是SSH方式,然后底下的凭证选择的是SSH方式的凭证,当然你现在还未设置所以没有凭证,点击add按钮,添加凭证。


然后接下来就是填上你在gitlab上的项目的用来这里Jenkins编译的某个分支,我这里是*/develop_1.0.1_Jenkins
接下来在Additional Behaviours中,这里填写的是timeout时间,即编译过程中最多等待多久,我这里是60分钟

3、Build Triggers
如果是单一的run一下,这里暂时可以不用配置,如果以后要定时构建以及当有新的提交时自动构建,则需要在这里配置。

4、Build Environment
编译环境这里首先是选择xcode路径(可用于不同版本的xcode并存时使用)。


接下来配置好之前设置过的keychain和描述文件。

5、Build
最后就是最为重要的脚本编译过程的命令。


前提:执行几步就创建几个shell命令模块。
我这里分3块。
1、cd到指定目录,先将pod install
具体命令:
#bin/bsah - l

export LANG=en_US.UTF-8

export LANGUAGE=en_US.UTF-8

export LC_ALL=en_US.UTF-8

cd /Users/xxxxxx(你的项目根目录,记住这里是你的Jenkins目录下的项目,不是你平时开发的目录,你用Jenkins跑过后会将你之前设置的分支的项目拷贝到  .jenkins/workspace 下,所以是这个目录下你的项目根目录)

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

2、利用xcode build命令执行archive和export命令

xcodebuild -archivePath "/Users/xxx(用户)/.jenkins/workspace/xxx(项目目录)/build/Debug-iphoneos/xxx.xcarchive(archive文件)" -workspace GHistory.xcworkspace -sdk iphoneos -scheme "xxx(scheme名)" -configuration "Debug" archive

xcodebuild -exportArchive -archivePath "/Users/xxx(用户)/.jenkins/workspace/xxx(项目目录)/build/Debug-iphoneos/xxx.xcarchive(archive文件)" -exportPath "/Users/xxx(用户)/.jenkins/workspace/xxx(项目目录)/xxxx(随便搞个文件夹路径用来存储导出的ipa文件路径)" -exportOptionsPlist ('exportOptionsPlist 路径,后面会讲这个路径在哪。)' -allowProvisioningUpdates

备注:xcode9之前,exportOptionsPlist 可以自己生成文件来处理,但是在xcode9开始,会报如下错误:error: exportArchive: "AppName.app" requires a provisioning profile with the Push Notifications and App Groups features. Error Domain=IDEProvisioningErrorDomain Code=9 "AppName.app" requires a provisioning profile with the Push Notifications and App Groups features." UserInfo={NSLocalizedDescription="AppName.app" requires a provisioning profile with the Push Notifications and App Groups features., NSLocalizedRecoverySuggestion=Add a profile to the "provisioningProfiles" dictionary in your Export Options property list.} // 或 "Error Domain=IDEProvisioningErrorDomain Code=9 \"\"ios-simple-objc.app\" requires a provisioning profile.\" UserInfo={NSLocalizedDescription=\"ios-simple-objc.app\" requires a provisioning profile., NSLocalizedRecoverySuggestion=Add a profile to the \"provisioningProfiles\" dictionary in your Export Options property list.}",解决方案就是需要使用Xcode9先手动构建一次,使用构建生成的plist配置就可以完成export操作。解决连接传送门:Xcode9 xcodebuild export plist 配置,这里面有一个没说清的,就是在xcode10中手动export操作来生成exportOptionsPlist的过程中,没有export入口,而是distribute app入口,流程如下:

这里一定要选择所有设备这个选项,因为选择了特定的设备,生成的ipa文件名称是特定字符串名,你在下面的蒲公英上传过程中指定路径时就不好搞了。
后续就是和链接里所说的基本一致了。

3、拿到上面export出来的ipa包,上传蒲公英

uKey="你的蒲公英userkey"
apiKey="你的蒲公英apikey"
IPA_PATH="IPA地址"
curl -F "file=@${IPA_PATH}" -F "uKey=${uKey}" -F "_api_key=${apiKey}" https://www.pgyer.com/apiv1/app/upload

至此,整个项目的流程配置就完成了。

Jenkins项目运行

回到首页,在项目背后点击即可运行,或者进入项目里面点击立即构建。
开始构建之后,下面就会出现对应序号的构建记录 点击正在构建的记录,可以看到实时的日志输出,通过日志输出可以定位构建失败的原因。 项目前面的天气指代项目运行的成功率。

如果要下班了,结束Jenkins,在终端里面control + c即可,下次再次启动Jenkins同样需要打开终端输入Jenkins`。

看完前面的各种流程,其实Jenkins的工作流可以总结为下图:

常见错误及过程中遇到的坑 盘点

1、
错误:我们项目(主要OC)混编,里面加入了一个charts库(swift,用来做图表的),原来是直接拖到项目里去运行的,后来Jenkins死活编译错误Jenkins error: no such module,报错显示是swift编译过程错误。
解决方案:于是无奈删掉这个库,换成cocoapods形式导入,最后OK。而这个过程比较曹丹的是网上很多cocoapods集成charts的文章都是错的,这里有一个ok的,送上:正确使用cocoaPods 在OC中集成Charts第三方库
2、
错误:Cannot add a "GitLab API Token" as a credential
解决方案:https://github.com/jenkinsci/gitlab-plugin/issues/388
3、
错误:** ARCHIVE SUCCEEDED **,但是** EXPORT FAILED **,报错信息是:xcodebuild[94600:4150551] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/rr/3kswglw96vq9ky4xhcmbyrch0000gn/T/XXXX_2018-11-19_19-50-52.082.xcdistributionlogs'. error: exportArchive: The data couldn’t be read because it isn’t in the correct format. Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value., NSFilePath=/var/folders/rr/3kswglw96vq9ky4xhcmbyrch0000gn/T/ipatool-json-filepath-Y5R9WN}
解决方案:网上很多文章说是bitcode的原因,什么导出的exportOptionsPlist文件要关闭bitcode、pods工程要关闭bitcode,在我这里都不适用。最终发现问题出在target支持的iOS系统最低版本不一致上。工程从10.0开始支持,但是cocoapods的podfile里面platform :ios, '8.0'还是支持的8.0,所以这里同步一下,然后重新pod install一下,提交到Jenkins需要编译的分支,重新构建即可。更新:后续偶尔还会出现这个错误,最后再修改一个东西即可:在手动distribute app的时候,APP Thinning这里选择none而不是All compatible device去获取exportOptionsPlist文件,这个时候自动打包导出的地址会有所变化,不再是原来的APPS路径下,而是直接在外面,所以蒲公英设置的时候需要注意。
4、
错误:The path to store mobile provisioning profile files on the master is not configured. Go the plugin main configuration page and give the path.
解决方案:这个的原因是虽然添加了描述文件,但是没有添加本地描述文件地址,首先确保已经双击过你下载的描述文件,这样在/Users/perfect/Library/MobileDevice/Provisioning Profiles这个路径下才会出现你下载的描述文件,然后把这个地址粘贴到Keychains and Provisioning Profiles Management里面(仔细找)。
5、
错误:在执行pod install过程中,出现报错:

+ /usr/local/bin/pod install --verbose --no-repo-update
/System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/rubygems/dependency.rb:319:in `to_specs': Could not find 'minitest' (~> 5.1) among 33 total gem(s) (Gem::LoadError)

解决方案:cocoapods出现错误,于是先cd到Jenkins下workspace目录中,手动执行一遍pod install,如果出现error: RPC failed; curl 18 transfer closed with outstanding read data remaining的错误,则说明是curl的postBuffer默认值太小,需要在终端重新配置这个值。先执行git config -l,看看出来的配置列表里面是否包含http.postBuffer选项,如果不包含,或者值不为524288000,则执行命令git config --global http.postBuffer 524288000,修改对应的值,改完后再查询一遍,成功后重新执行pod install即可。成功后再去Jenkins构建一遍吧。
最后确定为安装cocoapods的方式不对导致的,原来我是直接采取homebrew安装的,导致gem没更新。最后先卸载掉homebrew里面的cocoapods,brew uninstall cocoapods,然后再用gem的方式安装:sudo gem install cocoapods,然后重启Jenkins再去构建。
6、
错误:Build system information error: /Users/xxxx/.jenkins/workspace/GHistory/Pods/Pods/Target Support Files/Pods-GHistory/Pods-xxxx.debug.xcconfig: unable to open file (in target "xxxx" in project "xxxx") (in target 'xxxx')
解决方案:从上面的错误信息可以看出是pod出了问题。而且路径上出现了pods/pods这种,很明显是路径错了,于是我们找到工程中,修改对应的路径即可。(我这里的问题是因为我之前安装过一个坏的cocoapods,podinstall过,现在换了一个好的,但是路径它没有自动更替过来。)

。当然,由于你用的Jenkins,所以这里只是本地改了,本地可以run。既然根源是本地缓存路径没有自动更改,那么就删除Jenkins、workspace下对应的这个项目的内容,用Jenkins重新构建一遍。这也就意味着,更换了cocoapods后,需要删除本地目录中的项目,然后重新拉一遍才可。(会出现拉取下来还是一样的结果的情况,一种方式是修改后push到分支上,但是不是最优解,最优解有待研究...)

最后,如有其它报错,可在下方评论留言。

参考:
1、选择不同版本xcode
2、Jenkins 100次构建失败踩坑全录

相关文章

网友评论

    本文标题:自动化打包实战 — Jenkins

    本文链接:https://www.haomeiwen.com/subject/epuszftx.html