Jenkins好处:
- 缩减开发周期,快速迭代版本
- 自动化流水线操作带来的高效
- 随时可部署
- 极大程度避免低级错误
Jenkins用处:
- 构建项目
- 跑测试用例检测bug
- 静态代码检测
- 部署
写在前面
建议公司配的Jenkins服务器电脑能让你的电脑对其远程操作,这样有利于你查阅资料以及保存图片什么的。
在文章的底部有这次安装到实现位遇到的各种错误,虽然过程中也有解答,但是下面的错误是比较耗时的,如果你遇到了问题,可以去本文的问题列表观看一下。
安装方法:
安装与准备
1.安装Home brew
在这里统一采用Home brew安装,如果没有brew请参考安装方法;
2.安装java环境
安装java环境,在终端输入brew install Caskroom/cask/java
安装过程如图1显示:
安装后在终端执行brew cask reinstall java
操作,在这期间会让你输入用户密码,完成结果如图2
执行完毕之后,在终端输入brew cask install caskroom/versions/java8
,期间还会让你输入用户密码。完成结果如图3
3.安装jenkins
方法一
从Jenkins官网上下载最新的pkg安装包,如果是pkg安装,那么选择的时候不要安装到shared目录下,默认是shared目录下。(也可以下载jenkins.war, 然后运行Java -jar jenkins.war,进行安装。)
以前是使用pkg安装的,Mac下面会有很多权限的问题,使用brwe安装是新的安装方法,以前没有使用过,不过本次我会采用这个方法安装。
方法二:
使用brew安装,如果没有brew请参考安装方法,brew安装方法以及使用说明;
1.brew安装jenkins
brew install jenkins
2.链接 launchd 配置文件
ln -sfv /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents
如果要其他机器也可以访问,把ip地址改为广播地址:<string>--httpListenAddress=0.0.0.0</string>
3.然后执行命令
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
在执行这句命令的时候可能会报错,错误信息
/Users/apple/Library/LaunchAgents/homebrew.mxcl.jenkins.plist: Not a directory
那么该错误如何解决呢?方法如下,在终端输入cd Library
,找到LaunchAgents,这个LaunchAgents现在可能是一个plist文件,复制这个文件名字,删除该plist文件,<mark>创建文件夹LaunchAgents</mark>,然后重新执行2号命令
4.命令行启动Jenkins
jenkins
5.打开浏览器,输入 localhost:8080 就可看到Jenkins的web界面,如果你更换了端口就是你后来设置的端口
安装Jenkins遇到的问题
1.在终端不启动Jenkins的情况下(没有在终端输入Jenkins回车的情况),在Safari输入localhost:8080,却说找不到页面的情况,是因为没有启动Jenkins服务,解决方法是开机默认启动Jenkins
设置开机启动
sudo launchctl load -w /Library/LaunchDaemons/org.jenkins-ci.plist
取消开机启动
sudo launchctl unload -w /Library/LaunchDaemons/org.jenkins-ci.plist
2.Jenkenis报错:该jenkins实例似乎已离线,解决方法如下
安装插件那个页面,就是提示你offline的那个页面,不要动。然后打开一个新的tab,输入网址http://localhost:8080/pluginManager/advanced。 这里面最底下有个【升级站点】,把其中的链接改成http的就好了,http://updates.jenkins.io/update-center.json。 然后在服务列表中关闭jenkins,再启动,这样就能正常联网了
另附上更换端口命令,该命令如果不能更改端口,请更改命令上的路径,对应上你的这个文件
sudo defaults write /Library/Preferences/org.jenkins-ci httpPort 7070
配置Jenkins
1.登陆页面
打开Jenkins后会让去一个填写token的页面如图4,存储token的地方就是图片上那行红色字体目录下,使用终端 open + 红色字体就看到了2.安装插件
打开系统管理,管理插件详见图6选择插件,如果你的代码是在gitlab上面,在“可选插件”中选中“GitLab Plugin”和“Gitlab Hook Plugin”这两项,然后安装,安装完毕后,再次选择Xcode插件,在“可选插件”中选中“Xcode integration”安装,其他插件根据你的需求选择即可。
3.配置Keychains and Provisioning Profiles Management
接下来配置Keychains and Provisioning Profiles Management,根据顺序选择系统管理>Keychains and Provisioning Profiles Management如图7
需要上传login.keychain文件,该文件获取方法,在终端中输入
cd ~/Library/Keychains
在终端键入ls详见图8
根据需要将证书添加进去即可,但是由于macOS10.12以及以后的系统里面没有login.keychain文件,只有login.keychain-db,可以复制出来删除-db,也可以创建一个快捷方式名字叫做login.keychain,upload就好了。
然后添加Provisioning Profiles,上传方法和上传login.keychain一样,去选择Provisioning Profiles文件,然后upload,然后结果如下图,蓝色框内的是固定格式的/Users/用户名/Library/MobileDevice/Provisioning Profiles
创建一个任务
然后创建一个任务,自由风格的任务,因为构建方法会有两种,除了共同的地方,构建会分为两部分解答,第一部分是macOS10.12以前的构建方法,但是由于Jenkins的Xcode和Mac的系统版本问题,所以建议使用第二种方法。
General
创建一个自由风格的任务,然后在选择丢弃旧的构建,至于天数和保持的最大个数,按照自己的需求来就好,如图11
源码管理
接下来是源码管理,在Repository URL里面添加你的git地址,我这里添加的是http的,如果你的项目是使用的ssh的,那么就将git开头的地址填写上,然后店家Add添加你的git帐号,如果你的事ssh的,将ssh的密匙填写上,具体的自己百度一下就好咯,我就不多写了,结果如图12
构建触发器
接下来是构建触发器,也就是什么时候触发自动打包我这里填写的是H 20 * * *
这个意思就是H小时然后,后面跟着数字,在后面就是日月年,*
代表的我认为是每次都触发,也就是每天每月每年,但是Jenkins的时间不是绝对的,一般都是在随机在半点,也就是设置20点,大概会在20:30分左右会触发,如果需要两个时间,那么格式可以这样H 20,22 * * *
结果如图13
构建环境
在构建环境里面勾选Keychains and Code Signing Identities和Mobile Provisioning Profiles,Keychains and Code Signing Identities是打包需要的证书,Mobile Provisioning Profiles是打包需要的配置文件,都是可以自己选择的,我的如图14
<mark>接下来就是构建了,因为Jenkins对新版的Xcode插件兼容不好,只能用脚本来打包,但是老版本的Xcode可以使用Jenkins的Xcode插件,下面将构建一位使用Xcode插件的,构建二是脚本的</mark>
构建一,使用Xcode插件
在构建里面点击增加构建步骤,然后点击Xcode.
General build settings
在Target里面填写你的项目名称,Clean before build勾选YES,勾选Pack application, build and sign .ipa?,然后会有新的选项Export method是你要打的包的类型,就是你在手动打包的时候选额的ad-hoc或者Appstore那四个选项,这个按照你要打的类型填写,.ipa filename pattern是你打出包ipa的名字,我的这里填写了项目名字和-$(BUILD_DATE)
,意思就是在后面追加时间,Output directory是导出ipa的目录,如果不填写,会在Jenkins默认的目录.
详见图15
Code signing & OS X keychain options
勾选Unlock Keychain?,在Keychain path那里填写${HOME}/Library/Keychains/login.keychain
,意思是找到你的login.keychain(登陆钥匙串),如果你的事复制出来改的名字,那么就填写你相对应的目录,Keychain password就是你电脑的登陆密码。详见图16
Advanced Xcode build options
勾选Clean test reports?,如果你使用了cocoapods那么填写Xcode Workspace File,如果没有使用cocoapods填写Xcode Project Directory,然后填写Build output directory就是你到处ipa的路径,详见图17
构建二,使用脚本
因为Jenkins对现在的Xcode9插件兼容性不好,打不了包,所以我们使用了xcodebuild打包,下面是我的脚本,并且里面附上了自己非常详细的注释,可能有点啰嗦。
#!/bin/sh
#因为Jenkins打包可能是自动的,那么build号是不会自己再去修改然后push到git上面的,所以这个buildPlist就是修改build号的路径。
buildPlist="/Users/apple/.jenkins/workspace/longxin_a/eCloud/Build/LongHu/Config/eCloud-Info.plist"
#这个获取现在的 月日时分 用它来做build号
buildNumber=$(date +"%m%d%H%M")
#修改plist文件需要/usr/libexec/PlistBuddy -c命令,CFBundleVersion是修改的这个build号,$buildNumber是你要修改的数值,$buildPlist是你修改哪个地方的plist文件。
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$buildPlist"
#这个是获取当前的build号,本来是用来看看有没有修改成功的
#newBuildName=$(/usr/libexec/PlistBuddy -c "print :CFBundleVersion" "$buildPlist")
#这个是打印,带自动换行的打印
echo $newBuildName
#因为我怕他修改plist的时候需要时间,所以索性在这里我让他等了3秒,当然你也可以去掉
sleep 3
#这个buildPath是到时候我生成xcarchive文件的路径和打ipa时候需要找到xcarchive的路径
buildPath="/Users/apple/.jenkins/workspace/longxin_a/build/Release-iphoneos/eCloud.xcarchive"
#这个路径是我生成ipa的路径
ipaPath="/Users/apple/Documents/longhuBuild/"
#这个是ExportOptions.plist的路径,有这个就不用在用脚本写证书了,这个plist你只要手动打过包,那么在生成ipa的文件夹里面就会有,找一个自己不经常修改的地方放在那里,写上这个路径就好,当然如果你不想这么做,想用shell语言设置证书,我会在 问题 列表里面有介绍
exportOptionsplistPath='/Users/apple/.jenkins/workspace/ExportOptions.plist'
#因为我使用了cocoapods所以这里用的-workspace,如果你没有使用cocoapods使用-project,下面的命令都一样
#这个命令主要是用来clean,clean的是Release的路径,clean的是/Users/app***eCloud.xcworkspace路径的eCloud,
xcodebuild -workspace /Users/apple/.jenkins/workspace/longxin_a/eCloud.xcworkspace -scheme eCloud -configuration "Release" clean
#这个是生成xcarchive,Release的
xcodebuild -workspace /Users/apple/.jenkins/workspace/longxin_a/eCloud.xcworkspace -scheme eCloud -archivePath ${buildPath} -configuration "Release" archive
#这个是将xcarchive文件打包成ipa
xcodebuild -exportArchive -archivePath ${buildPath} -exportPath ${ipaPath} -exportOptionsPlist ${exportOptionsplistPath} -allowProvisioningUpdates
邮箱通知
到这里,其实你就已经打包成功了,但是打包成功后是不是我们需要通知一些人呢?Jenkins是有邮件通知的。
现在开始设置,首先你已经安装了插件Email Extension Plugin,这个在插件那里直接安装就好这是第一步;
然后进入系统管理->系统设置找到Jenkins Location模块,在系统管理员邮件地址填写你的系统管理邮箱,这个邮箱是你发送通知邮件的邮箱,
然后找到Extended E-mail Notification模块
填写方法如图18图19
然后找到构建后操作,点击增加构建后的操作步骤点击Editable Email Notification,在Project From里面写上管理者邮箱,也就是发送邮件的邮箱,然后点击Advanced Setting;
里面有三个选项,分别是你在系统设置里面勾选的那几个,根据需求填写就好,我这里填写的是Always,也就是无论构建成功还是失败,Recipient List是接收者的邮箱,这里多个邮箱用英文逗号隔开——','详见图20
上传到fir
先去下载fir插件
然后在Jenkins里面点击系统管理->管理插件->高级,然后滑动到上传插件那里,选择刚才下载的插件,点击上传,等待上传成功后,进入到你的项目配置里面滑动到最下面,也就是找到构建后操作,点击增加构建后操作步骤,选择Upload to fir.im,打开你的浏览器,打开fir官方网站,获取方法见图21
然后输入你的IPA/APK Files (optional)这个是你ipa的路径,如果不选择,会是Jenkins默认的路径
这里有fir的官方文档,根据fir的官方文档即可就可以Jenkins上传到fir文档
问题
-
进入目录
~/Library/Keychains
找不到login.keychain文件(在macOS High Sierra 10.12以后找不到login.keychain),但是Keychains and Provisioning Profiles Management里面之能上传 keychain 和 mobileprovision 格式的文件,我的做法是两种办法:1.login.keychain-db复制到桌面,然后改名字为login.keychain;
2.在终端输入
ln -s ~/Library/Keychains/login.keychain-db ~/Library/Keychains/login.keychain
制作一个login.keychain-db的替身,也就是俗称的快捷方式。
- 控制台报错输出
FATAL: String index out of range: 15
java.lang.StringIndexOutOfBoundsException: String index out of range: 15
at java.lang.String.substring(String.java:1963)
at com.sic.plugins.kpp.provider.KPPBaseProvisioningProfilesProvider.removeUUIDFromFileName(KPPBaseProvisioningProfilesProvider.java:171)
at com.sic.plugins.kpp.model.KPPProvisioningProfile.getProvisioningProfileFilePath(KPPProvisioningProfile.java:76)
at com.sic.plugins.kpp.KPPProvisioningProfilesBuildWrapper.copyProvisioningProfiles(KPPProvisioningProfilesBuildWrapper.java:157)
at com.sic.plugins.kpp.KPPProvisioningProfilesBuildWrapper.setUp(KPPProvisioningProfilesBuildWrapper.java:99)
at hudson.model.BuildAbstractBuildExecution.run(AbstractBuild.java:504)
at hudson.model.Run.execute(Run.java:1794)
at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:429)
Finished: FAILURE
这是因为当时没有在Keychains and Provisioning Profiles Management里面上传配置文件导致的,上传配置文件的方法和上传login.keychain一样,选择那个配置文件,点击upload,配置文件存在你电脑的`/Users/‘电脑用户名称’/Library/MobileDevice/Provisioning Profiles`目录下。
-
xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance
FATAL: Check your Xcode installation. Jenkins cannot retrieve its version.
Build step 'Xcode' marked build as failure
Finished: FAILURE这个问题是说我的Xcode路径不对,在终端输入
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/
解决。
如果要查看Xcode现在的安装路径,使用命令行xcode-select -print-path
-
错误
== Available schemes
[Jenkins的项目名字] (WORKSPACE)/eCloud.xcworkspace
xcodebuild: error: '$(WORKSPACE)/eCloud.xcworkspace' does not exist.Build step 'Xcode' marked build as failure
Finished: FAILURE这个错误是说我们选择的项目路径,也就是需要打包的工作路径、文件不对,修改方法为,
在Jenkins项目配置里面,找到构建,点击增加构建步骤,选择Advanced Xcode build options,找到Xcode Workspace File填写你的工作路径,如过你是使用这篇文章的安装方法安装的Jenkins,也就是Home brew安装的,那么你的工作路径应该在/Users/用户/.jenkins/workspace/Jenkins创建的项目/项目名称
,比如我们的项目叫做'Almost',用户是'liuxiansheng',在Jenkins上创建的项目叫做'Almost_a',那么这个路径应该是/Users/liuxiansheng/.jenkins/workspace/Almost_a/Almost
另附图--路径不对
- 错误
The following build commands failed:
PhaseScriptExecution [CP]\ Check\ Pods\ Manifest.lock /Users/apple/Library/Developer/Xcode/DerivedData/eCloud-bdxpnthrsouuyofxsgtiblngntsr/Build/Intermediates.noindex/ArchiveIntermediates/eCloud/IntermediateBuildFilesPath/eCloud.build/Release-iphoneos/eCloud.build/Script-EE80A86E85F13053B6498D87.sh
(1 failure)
Build step 'Xcode' marked build as failure
Finished: FAILURE
- Jenkins报出错误
diff: /Podfile.lock: No such file or directory
diff: /Manifest.lock: No such file or directory
error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
** ARCHIVE FAILED **
The following build commands failed:
PhaseScriptExecution [CP]\ Check\ Pods\ Manifest.lock /Users/apple/Library/Developer/Xcode/DerivedData/eCloud-bdxpnthrsouuyofxsgtiblngntsr/Build/Intermediates.noindex/ArchiveIntermediates/eCloud/IntermediateBuildFilesPath/eCloud.build/Release-iphoneos/eCloud.build/Script-EE80A86E85F13053B6498D87.sh
(1 failure)
Build step 'Xcode' marked build as failure
Finished: FAILURE
但是我们从Jenkins上面基本看不出什么错误信息来,打开终端进入Jenkins工作目录,我们根据Jenkins的错误提示得知是因为Jenkins不能顺利执行pod install
所以,我们在目录下执行这个命令,得到错误,因为我自己的错误丢失了,具体的也就不说了,说一下解决方案和过程,当使用pod install
之后,会列出你的系统ruby、cocoapods、系统、xcode、java等版本,我是使用sudo命令将cocoapods卸载,从新安装,使用rvm list known和rvm list查询自己的安装记录和现有的版本,如果不是最新的,使用rvm install ruby最新版本
,完成后使用 rvm use ruby最新版本
切换,然后就可以pod成功,Jenkins也就不会报那个错误了。
在这里再说一句,切换ruby的时候可能你要使用rvm use ruby最新版本 --default
命令,这个是切换ruby版本并设成默认
- Jenkins报错
error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
明明我已经升级了ruby,也亲自使用命令行pod install
成功了,那么为什么还报这个错误呢,好,到这里先找一下错误的原因,在你的项目目录下打开Podfile.lock和Manifest.lock文件对比一下,是不是不一样,尤其是最后一行COCOAPODS: 1.5.2
可能不一样,版本要对象,出现这个问题就是你们协同开发的时候没有忽略文件,这个文件是不需要上传到git上面的,自己本地有就行了,因为每个人cocoapods版本可能都不是一样的,解决方案,创建.gitignore文件,将Podfile.lock忽略。
那么问题又来了,可能你将Podfile.lock加入了.gitignore文件里面,但是不生效啊,解决方案跳转到git基本操作、stash暂存、stash找回、创建分支、git忽略文件按照git忽略文件就好了
- Jenkins报出错误
** ARCHIVE SUCCEEDED **
Cleaning up previously generated .ipa files
Cleaning up previously generated .dSYM.zip files
Packaging IPA
[longxin_a] $ /usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleVersion" /Users/apple/Documents/longhuBuild/eCloud.xcarchive/Info.plist
[longxin_a] $ /usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleShortVersionString" /Users/apple/Documents/longhuBuild/eCloud.xcarchive/Info.plist
Packaging eCloud.xcarchive => /Users/apple/Documents/longhuBuild/eCloud-$(BUILD_DATE).ipa
[longxin_a] $ /usr/bin/xcodebuild -exportArchive -archivePath /Users/apple/Documents/longhuBuild/eCloud.xcarchive -exportPath /Users/apple/Documents/longhuBuild -exportOptionsPlist /Users/apple/Documents/longhuBuild/ad-hocExportOptions.plist -allowProvisioningUpdates
2018-05-28 15:43:33.337 xcodebuild[91233:333051] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/xg/wlxwht_j4yg08771094c51g40000gn/T/eCloud_2018-05-28_15-43-33.336.xcdistributionlogs'.
2018-05-28 15:43:33.697 xcodebuild[91233:333051] [MT] IDEDistribution: Step failed: <IDEDistributionSigningAssetsStep: 0x7f80153019d0>: Error Domain=IDEDistributionSigningAssetStepErrorDomain Code=0 "Locating signing assets failed." UserInfo={NSLocalizedDescription=Locating signing assets failed., IDEDistributionSigningAssetStepUnderlyingErrors=(
"Error Domain=IDEProvisioningErrorDomain Code=9 \"\"eCloud.app\" requires a provisioning profile with the Push Notifications and App Groups features.\" UserInfo={NSLocalizedDescription=\"eCloud.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 \"\"eCloudShareExtension.appex\" requires a provisioning profile with the App Groups feature.\" UserInfo={NSLocalizedDescription=\"eCloudShareExtension.appex\" requires a provisioning profile with the App Groups feature., NSLocalizedRecoverySuggestion=Add a profile to the \"provisioningProfiles\" dictionary in your Export Options property list.}"
)}
error: exportArchive: "eCloud.app" requires a provisioning profile with the Push Notifications and App Groups features.
Error Domain=IDEProvisioningErrorDomain Code=9 ""eCloud.app" requires a provisioning profile with the Push Notifications and App Groups features." UserInfo={NSLocalizedDescription="eCloud.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: exportArchive: "eCloudShareExtension.appex" requires a provisioning profile with the App Groups feature.
Error Domain=IDEProvisioningErrorDomain Code=9 ""eCloudShareExtension.appex" requires a provisioning profile with the App Groups feature." UserInfo={NSLocalizedDescription="eCloudShareExtension.appex" requires a provisioning profile with the App Groups feature., NSLocalizedRecoverySuggestion=Add a profile to the "provisioningProfiles" dictionary in your Export Options property list.}
** EXPORT FAILED **
Failed to build /Users/apple/Documents/longhuBuild/eCloud-$(BUILD_DATE).ipa
Build step 'Xcode' marked build as failure
Finished: FAILURE
这个错误是你的证书没有匹配上,有几种可能,可能你的证书添加的不对,可能你的shell语言不对,可能添加对了,但是在构建环境的时候选择错了,我遇到的是配置文件没有添加。
- Jenkins报出错误
note: Execution policy exception registration failed and was skipped: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" (in target 'xxxx-xxxx' from project 'Pods')
这个问题出现的可能较小,出现这个问题的原因是,当你的打包机更新了新的证书或者需要执行信任的文件时,需要在本地编译一下,执行信任,并总是允许。
----------------------------------