昨天一位前同事问我,之前自动生成ios安装包是怎么弄的?网上找不到比较好的教程。这个搭建过程我是整理成文档的,但没有记录到博客中,趁今天稍微空点,把文档记录到博客上,方便需要的朋友借鉴参考。
一听说自动打包,可能很多人就想到是Jenkins,这边的方案确实也是使用Jenkins,那么,先简单介绍下Jenkins相关的关键字(持续集成、CI)
持续集成是一种软件开发实践:许多团队频繁地集成他们的工作,每位成员通常进行日常集成,进而每天会有多种集成。每个集成会由自动的构建(包括测试)来尽可能快地检测错误。许多团队发现这种方法可以显著的减少集成问题并且可以使团队开发更加快捷。
CI是一种开发实践。实践应该包含3个基本模块,一个可以自动构建的过程,自动编译代码,可以自动分发,部署和测试。一个代码仓库,SVN或者Git。最后一个是一个持续集成的服务器。通过持续集成,可以让我们通过自动化等手段高频率地去获取产品反馈并响应反馈的过程。
那么持续集成能给我们带来些什么好处呢?这里推荐一篇文章,文章中把Continuous integration (CI) and test-driven development (TDD)分成了12个步骤。
CI带来的好处有:
每个版本开始都会估算好开发周期,但是总会因为各种事情而延期。这其中包括了一些客观因素。由于产品线增多,迭代速度越来越快,给测试带来的压力也越来越大。如果测试都在开发完全开发完成之后再来测试,那就会影响很长一段时间。这时候由于集成晚就会严重拖慢项目节奏。如果能尽早的持续集成,尽快进入上图的12步骤的迭代环中,就可以尽早的暴露出问题,提早解决,尽量在规定时间内完成任务。
其实打包对于开发人员来说是一件很耗时,而且没有很大技术含量的工作。如果开发人员一多,相互改的代码冲突的几率就越大,加上没有产线管理机制,代码仓库的代码质量很难保证。团队里面会花一些时间来解决冲突,解决完了冲突还需要自己手动打包。这个时候如果证书又不对,又要耽误好长时间。这些时间其实可以用持续集成来节约起来的。一天两天看着不多,但是按照年的单位来计算,可以节约很多时间!
有了持续集成以后,我们可以以天为单位来打包,这种高频率的集成带来的最大的优点就是可以随时部署上线。这样就不会导致快要上线,到处是漏洞,到处是bug,手忙脚乱弄完以后还不能部署,严重影响上线时间。
我们可以犯错误,但是犯低级错误就很不应该。这里指的低级错误包括以下几点:编译错误,安装问题,接口问题,性能问题。
以天为单位的持续集成,可以很快发现编译问题,自动打包直接无法通过。打完包以后,测试扫码无法安装,这种问题也会立即被暴露出来。接口问题和性能问题就有自动化测试脚本来发现。这些低级问题由持续集成来暴露展现出来,提醒我们避免低级错误。
Jenkins 是一个开源项目,提供了一种易于使用的持续集成系统,使开发者从繁杂的集成中解脱出来,专注于更为重要的业务逻辑实现上。同时 Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和提醒功能,还能用图表的形式形象地展示项目构建的趋势和稳定性。
根据官方定义,Jenkins有以下的用途:
构建项目
跑测试用例检测bug
静态代码检测
部署
关于这4点,实际使用中还是比较方便的:
1.构建项目自动化打包可以省去开发人员好多时间,重要的是,Jenkins为我们维护了一套高质量可用的代码,而且保证了一个纯净的环境。我们经常会出现由于本地配置出错而导致打包失败的情况。现在Jenkins就是一个公平的评判者,它无法正确的编译出ipa,那就是有编译错误或者配置问题。开发人员没必要去争论本地是可以运行的,拉取了谁谁谁的代码以后就不能运行了。共同维护Jenkins的正常编译,因为Jenkins的编译环境比我们本地简单的多,它是最纯净无污染的编译环境。开发者就只用专注于编码。这是给开发者带来的便利。
2.这个可以用来自动化测试。在本地生成大批的测试用例。每天利用服务器不断的跑这些用例。每天每个接口都跑一遍。看上去没必要,但是实际上今天运行正常的系统,很可能由于今天的代码改动,明天就出现问题了。有了Jenkins可以以天为单位的进行回归测试,代码只要有改动,Jenkins就把所有的回归测试的用例全部都跑一遍。在项目工期紧张的情况下,很多情况测试都不是很重视回归测试,毕竟很可能测一遍之后是徒劳的“无用功”。然而由于回归测试不及时,就导致到最后发版的时候系统不可用了,这时候回头查找原因是比较耗时的,查看提交记录,看到上百条提交记录,排查起来也是头疼的事情。以天为单位的回归测试能立即发现问题。测试人员每天可以专注按单元测试,一周手动一次回归测试。这是给测试者带来的便利。
3.这个是静态代码分析,可以检测出很多代码的问题,比如潜在的内存泄露的问题。由于Jenkins所在环境的纯净,还是可以发现一些我们本地复杂环境无法发现的问题,进一步的提高代码质量。这是给质检带来的便利。
4.随时部署。Jenkins在打包完成之后可以设定之后的操作,这个时候往往就是提交app到跑测试用例的系统,或者部署到内测平台生成二维码。部署中不能安装等一些低级问题随之立即暴露。测试人员也只需要扫一下二维码即可安装,很方便。这也算是给测试带来的便利。
从官网https://jenkins.io/ 上下载最新的pkg安装包,我们这里选择LTS Release(稳定版本)下载,如下图所示
下载完成后进行安装,安装方式比较简单,这里就不一一说明
也可以下载jenkins.war, 然后运行Java -jar jenkins.war,进行安装。
安装完成后检查下jenkins是否安装成功:
正常安装完成后,Safari可能会自动打开,如果没有自动打开,打开浏览器,输入http://localhost:8080
,jenkins默认的端口是8080,如果8080端口被占用要更换端口,请自行网上查找,这里就不一一说明了。
打开后的页面如果是如下图所示,即说明安装成功。
如果没有出现预期的页面,出现了如下页面
排查方法可以如下参考:
1、 先确认下jenkins进程是否存在,在终端输入jps或ps –ef | grep jenkins均可确认
这里截图说明jenkins是正常的
2、 检查下java环境是否正常,具体检查方法自行查找
3、 查看jenkins日志
这里要注意的是,jenkins在MAC OS跟centos或red linux不同环境下的配置文件和安装目录内的文件是有区别的。
MAC OS下,jenkins默认配置文件是/Library/LaunchDaemons/org.jenkins-ci.plist,内容如下:
<plist version="1.0">
<dict>
<key>StandardOutPathkey>
<string>/var/log/jenkins/jenkins.logstring>
<key>StandardErrorPathkey>
<string>/var/log/jenkins/jenkins.logstring>
<key>EnvironmentVariableskey>
<dict>
<key>JENKINS_HOMEkey>
<string>/Users/Shared/Jenkins/Homestring>
dict>
<key>GroupNamekey>
<string>daemonstring>
<key>KeepAlivekey>
<true/>
<key>Labelkey>
<string>org.jenkins-cistring>
<key>ProgramArgumentskey>
<array>
<string>/bin/bashstring>
<string>/Library/Application Support/Jenkins/jenkins-runner.shstring>
array>
<key>RunAtLoadkey>
<true/>
<key>UserNamekey>
<string>jenkins/string>
<key>SessionCreatekey>
<true/>
dict>
plist>
这个时候如果你重启电脑会发现Jenkins给你新增了一个用户,名字就叫Jenkins,不过这个时候你不知道密码。你可能会去试密码,肯定是不对的,因为初始密码很复杂。这个时候正确做法是打开http://localhost:8080
会出现下图的重设初始密码的界面。
按照提示,找到/Users/Shared/Jenkins/Home/ 这个目录下,这个目录虽然是共享目录,但是有权限的,非Jenkins用户/secrets/目录是没有读写权限的。
打开initialAdminPassword文件,复制出密码,就可以填到网页上去重置密码了。
另外一种方法就是直接使用root用户在终端输入命令查看sudo cat /Users/Shared/Jenkins/Home/secrets/initialAdminPassword,复制出密码,就可以填到网页上去重置密码了。如下图
选择第一个(建议安装插件)
一路安装过来,输入用户名,密码,邮件这些,就算安装完成了。
还是继续登录http://localhost:8080
,选择“系统管理”——“管理插件”,我们要先安装一些辅助插件。
因为我们用的是GitLab来管理源代码,Jenkins本身并没有自带GitLab插件,所以我们需要依次选择 系统管理->管理插件,在“可选插件”中选中“GitLab Plugin”和“Gitlab Hook Plugin”这两项,然后安装。
同安装GitLab插件的步骤一样,我们依次选择系统管理->管理插件,在“可选插件”中选中“Xcode integration”安装。
IOS项目打包需要签名文件和证书
安装完了这个,我们就可以配置一个构建项目了。
点击新建好的项目,进来配置一下General参数。
这里可以设置包的保留天数还有最大个数。
由于现在我用到的是GitLab,先配置SSH Key,在Jenkins的证书管理中添加SSH。在Jenkins管理页面,选择“Credentials”,然后选择“Global credentials (unrestricted)”,点击“Add Credentials”,如下图所示,我们填写自己的SSH信息,然后点击“Save”,这样就把SSH添加到Jenkins的全局域中去了。
我这边已经配置好了,可以点击左边的Credentials(证书)
鼠标移至jenkins出现倒三角,点击选择update
这里配置的是Gitlab账户对应的私钥,公钥需要在Gitlab里面进行配置。
检查ssh是否配置成功:
说明ssh配置成功
如果有下图的提示,就说明Jenkins还没有连通GitLab或者SVN,那就请再检查SSH Key是否配置正确。
这里是设置自动化测试的地方。这里涉及的内容很多,这里暂时先不设置。有自动化测试需求的可以好好研究研究这里的设置。
简单说明触发器几个配置项
Poll SCM (poll source code management) 轮询源码管理
需要设置源码的路径才能起到轮询的效果。一般设置为类似结果: 0/5 每5分钟轮询一次
Build periodically (定时build)
一般设置为类似: 00 20 * 每天 20点执行定时build 。当然两者的设置都是一样可以通用的。格式是这样的
分钟(0-59) 小时(0-23) 日期(1-31) 月(1-12) 周几(0-7,0和7都是周日)
iOS打包需要签名文件和证书,这部分的配置是很重要的,也遇到了不少坑,这里重点说明下。
这里是需要用到前面安装的Keychains and Provisioning Profiles Management插件,在系统管理页面,选择“Keychains and Provisioning Profiles Management”。
进入Keychains and Provisioning Profiles Management页面,点击“浏览”按钮,分别上传自己的keychain和证书。上传成功后,我们再为keychain指明签名文件的名称。点击“Add Code Signing Identity”,最后添加成功后如下图所示:
Keychain和证书可以找ios开发提供。
注意:我第一次导入证书和Provisioning Profiles文件,就遇到了一点小“坑”,我当时以为是需要证书,但是这里需要的Keychain,并不是cer证书文件。这个Keychain其实在/Users/管理员用户名/Library/keychains/login.keychain,当把这个Keychain设置好了之后,Jenkins会把这个Keychain拷贝到/Users/Shared/Jenkins/Library/keychains这里,(Library是隐藏文件)。Provisioning Profiles文件也直接拷贝到/Users/Shared/Jenkins/Library/MobileDevice文件目录下。
这样证书和签名文件就在Jenkins中配置好了,接下来我们只需要在item设置中指定相关文件即可。
回到我们新建的item,找到构建环境,按下图选好自己的相关证书和签名文件。
我这边编写的构建脚本如下图所示
这里遇到了各种各样的问题,到时候在文档结尾会附上
至此,我们的Jenkins设置就全部完成了。点击构建,就会开始构建项目了。
构建一次,各个颜色代表的意义如下:
天气的晴雨表代表了项目的质量,这也是Jenkins的一个特色。
如果构建失败了,可以去查看Console Output可以查看log日志。
当你开发iOS应用时,会经常使用到很多第三方开源类库,比如JSONKit,AFNetWorking等等。可能某个类库又用到其他类库,所以要使用它,必须得另外下载其他类库,而其他类库又用到其他类库,“子子孙孙无穷尽也”,这也许是比较特殊的情况。总之小编的意思就是,手动一个个去下载所需类库十分麻烦。另外一种常见情况是,你项目中用到的类库有更新,你必须得重新下载新版本,重新加入到项目中,十分麻烦。如果能有什么工具能解决这些恼人的问题,那将“善莫大焉”。所以,你需要 CocoaPods。
CocoaPods应该是iOS最常用最有名的类库管理工具了,上述两个烦人的问题,通过cocoaPods,只需要一行命令就可以完全解决,当然前提是你必须正确设置它。重要的是,绝大部分有名的开源类库,都支持CocoaPods。所以,作为iOS程序员的我们,掌握CocoaPods的使用是必不可少的基本技能了。
网上MAC OS安装cocopods的帖子一大把,这里就不赘述了,需要注意的就是要安装cocopods之前先要安装Ruby。这里就简单介绍下cocopods常用的几个命令:
初始化CocoaPods的环境
$ pod setup
提示:可能会花费较多的时间,你需要更多的耐心,跟你的网络状况有很大关系
检查CocoaPods安装版本
$ pod –version
在这儿如果你的电脑能够显示cocoapods版本号,那么恭喜你,你的CocoaPods安装成功了!!!
安装
$ pod install
升级
$ pod update
如果update 或 install 卡住了,可以用下面两个命令替代(前提是你已经把第三方库下载下来了)
$ pod install --verbose --no-repo-update
$ pod update --verbose --no-repo-update
xcodebuild 是苹果发布自动构建的工具。它在一个Xcode项目下能构建一个或者多个targets ,也能在一个workspace或者Xcode项目上构建scheme,总的来说,用它没错就是了。
这里简单介绍下常用的xcodebuild命令
查看可用的SDK
$ xcodebuild -showsdks
查看项目全部的targets,schemes和configurations
$ xcodebuild -list
使用xcodebuild clean [-optionName]…清除编译过程生成文件
xcodebuild clean -workspace XXX.xcworkspace -scheme XXX -configuration Debug -sdk iphoneos9.3
命令 | 说明 |
---|---|
-workspace NAME | 指定工作空间文件XXX.xcworkspace |
-scheme NAME | 指定构建工程名称 |
-configuration [Debug/Release] | 选择Debug或者Release构建 |
-sdk NAME | 指定编译时使用的SDK |
如:
$ xcodebuild clean -workspace XXX.xcworkspace -scheme XXX -configuration Debug -sdk iphoneos10.2
使用xcodebuild build [-optionName]…编译命令介绍(该命令结构适用于工作空间[workspace]编译)
xcodebuild build -workspace XXX.xcworkspace -scheme XXX -configuration Debug -sdk iphoneos9.3
命令 | 说明 |
---|---|
-workspace NAME | 指定工作空间文件XXX.xcworkspace |
-scheme NAME | 指定构建工程名称 |
-configuration [Debug/Release] | 选择Debug或者Release构建 |
-sdk NAME | 指定编译时使用的SDK |
使用xcodebuild archive [-optionName]… 编译并生成.xcarchive包
xcodebuild archive -archivePath /Users/UserName/Desktop/App/archive/XXX -workspace XXX.xcworkspace -scheme XXX -configuration Debug -sdk iphoneos9.3
命令 | 说明 |
---|---|
-archivePath PATH | 保存生成.xcarchive包路径 |
-workspace NAME | 指定工作空间文件XXX.xcworkspace |
-scheme NAME | 指定构建工程名称 |
-configuration [Debug/Release] | 选择Debug或者Release构建 |
-sdk NAME | 指定编译时使用的SDK |
使用xcodebuild -exportArchive [-optionName]… .archive包导出ipa文件
xcodebuild -exportArchive -archivePath /Users/UserName/Desktop/App/archive/XXX.xcarchive -exportPath /Users/UserName/Desktop/App/ipa/ -exportOptionsPlist /Users/UserName/Desktop/App/XXX.plist
命令 | 说明 |
---|---|
-archivePath | 选择要导出的.xcarchive包路径 |
-exportPath | 导出ipa保存目录 |
-exportOptionsPlist | 导出过程中需要的配置文件路径 |
配置文件简单说明,需要更多的配置文件说明请看官方文档
<plist version="1.0">
<dict>
<key>teamIDkey>
<string>88888*****string>
<key>methodkey>
<string>app-storestring>
dict>
plist>
配置文件key | 配置文件value |
---|---|
teamID | 选择相对应得证书ID(找ios开发提供) |
method | 导出ipa包类型:[app-store, ad-hoc, package, enterprise, development, developer-id] (测试环境包的话用development) |
构建脚本是用python写的,脚本内容如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import time
#******************需要手动填写***********************
#工作空间
workspaceName="XXX" + ".xcworkspace"
#构建工程
#schemeNames={"XXX","XXX","XXX"}
schemeNames={"XXX"}
#***************************************************
#获取当前时间并格式化
dateTime=time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime());
#获取当前目录路径
workspaceDirPath=os.getcwd()
#获取当前桌面路径
desktopPath=os.path.join(os.path.expanduser("~"), 'Desktop')
desktopPath2=os.path.expanduser("~/Desktop")
#最后生成基础目录
basePath=desktopPath+"/App"+dateTime
#archivePath目录
archivePath=basePath+"/archive"
if os.path.exists(archivePath)==False:
os.makedirs(archivePath)
#exportPath目录
exportPath=basePath+"/ipa"
if os.path.exists(exportPath)==False:
os.makedirs(exportPath)
for schemeName in schemeNames:
#print(schemeName)
#编译版本信息
sdkName="iphoneos10.2" #真机编译根据mac当前安装的SKD填写,不知道当前安装的是什么SDK版本使用xcodebuild -showsdks命令查看
configurationName="Debug" #debug版本[Debug|Release],使用xcodebuild -list查看configuration支持的类型
#BaseCommand
baseCommand=" -workspace "+workspaceName+" -scheme "+schemeName+" -configuration "+configurationName+" -sdk "+sdkName
#清除编译的app
xcodebuild_clean="xcodebuild clean"+baseCommand
#查看项目全部的targets,schemes和configurations
xcodebuild_list="xcodebuild -list"
#编译
xcodebuild_build="xcodebuild build"+baseCommand
#查看全部的SDK
xcodebuild_showsdks="xcodebuild -showsdks"
#archivePath命令
archivePathOption=" -archivePath "+archivePath+"/"+schemeName+".xcarchive"
#exportPath命令
exportPathOption=" -exportPath "+exportPath+"/"
#指定exportOptionsPlist文件路径
exportOptionsPlistOption=" -exportOptionsPlist "+workspaceDirPath+"/BuildAllApp.plist"
#编译并生成.archive包
xcodebuild_archive="xcodebuild archive"+archivePathOption+baseCommand
#.archive包导出ipa包
xcodebuild_exportArchive="xcodebuild -exportArchive"+archivePathOption+exportPathOption+exportOptionsPlistOption
期待已久的构建终于成功了,吐血啊~~~~
生成的ipa包即可获取到了
报错信息:[!] Invalid
Podfile
file: incompatible character encodings: UTF-8 and ASCII-8BIT. Updating CocoaPods might fix the issue.
解决方法:这是由于编码格式导致的,在终端输入如下export LC_CTYPE=”en_US.UTF-8”
原因可能有好几种:
淘宝镜像已经不可用了
需要使用最新的ruby镜像(https://gems.ruby-china.org/)
需要的命令行:
$ gem sources -r https://rubygems.org/ (移除旧版本的镜像,如果你不知道你电脑上目前用的是什么镜像,可用 $ gem sources -l 来查看)
$ gem sources -a https://gems.ruby-china.org/ (增加可用的镜像)
$ gem sources -l (用来检查使用替换镜像位置成功)
依赖的第三方库正在下载,可能比较大,加上网络原因,需要比较长的时间
输入”cd ~/.cocoapods/”命令行跳到cocoapods文件夹内,执行”du -sh *”查看正在下载的文件夹的大小
错误提示信息:
+ pod update --no-repo-update
/Users/Shared/Jenkins/tmp/hudson2918422419647221708.sh: line 11: pod: command not found
Build step 'Execute shell' marked build as failure
Finished: FAILURE
但直接在终端下执行此命令正常
解决办法:是由于jenkins构建时不会加载所需的环境变量,需要执行
export PATH=/Users/XXX/Development/apache-tomcat-8.0.29/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/XXX/Development/xcode_shell:/Users/XXX/.rvm/bin
PATH的内容可以从终端执行echo $PATH获取
在jenkins构建时,执行xcodebuild build命令最后会出现如下错误:
而在MAC OS那台服务器上执行此命令正常
解决办法:由于Xcodebuild命令打包时会调用Codesign进行签名,而此错误就是由于无法使用证书授权签名所致,可以在脚本中添加解锁。
执行命令security list-keychains
这里由于我的签名证书是在登陆选项处(通过钥匙串查看),选择login.keychain-db