Jenkins一:iOS自动打包完整实践

后续文章:Jenkins二:参数化构建iOS自动打包
快捷记录:如果jenkins是用dmg安装,因为权限问题不能访问keychain时,每次新添加一个p12文件都要如此,
/Users/管理员用户名/Library/keychains/login.keychain-db
将login.keychain-db复制一份并命名为login.keyain复制到
/Users/Shared/Jenkins/Library/Keychains/login.keychain
同时要在

前言 :

因团队发展涉及到需要把打包工作配置好自动打包后移交给测试团队,网上资料很多,找了几篇文章在我自己的MacBook Pro上跟着一步步安装下来,虽然也遇到几个问题,但很快就找到了解决方案最后成功自动打包成功,于是就在一台专门的打包电脑Mac mini上安装环境,因为都是随机在网上找的资料,结果两次安装环境步骤稍微不一样,第一次是使用homebrew安装的,安装在默认账户下,没遇到什么权限问题,第二次在mac mini上安装使用的是安装包的方式,遇到一系列问题,大部分问题跟权限有关。陆陆续续查找解决构建了200多次,持续了2个周

参考资料:

强烈建议使用homedrew安装会省却很多权限问题,如果用pkg安装包安装,会被权限问题折腾掉半条命
一、安装jenkins

前提条件:已经安装homedrew、Java

  • Mac终端软件安装利器:Homebrew
  • 如何在mac上安装java1.8
    通过homedrew安装jenkins命令
    brew install jenkins
    通过homedrew安装后jenkins安装好后所在的位置:
    实际安装位置:/usr/local/Cellar/jenkins/
    配置文件所在位置:/usr/local/opt/jenkins/
    工作空间位置:/Users/用户/.jenkins
    进入工作空间位置:cd ~/.jenkins
二、设置开机自启动:
方式一、将启动plist在LaunchAgents创建一个链接镜像(ln是创建一个link的意思)

1.1,创建一个链接到开机启动文件家里
ln -sfv /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents
1.2:手动启动jenkins
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
1.3 手动关闭jenkins
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
1.4 根据需求修改jenkins的启动端口,修改/usr/local/Cellar/jenkins/2.149/homebrew.mxcl.jenkins.plist即可
修改后重新启动可能会报如下错误:

/usr/local/Cellar/jenkins/2.149/homebrew.mxcl.jenkins.plist: Path had bad ownership/permissions

执行以下的两个命令行:

sudo chown root /usr/local/Cellar/jenkins/2.149/homebrew.mxcl.jenkins.plist
sudo chgrp wheel /usr/local/Cellar/jenkins/2.149/homebrew.mxcl.jenkins.plist
方式二(缺点:执行shell的termenal一直在前台运行,不小心关掉了就会停止,有强迫症爱干净的人也不喜欢多一个不能关掉的termenal窗口)

添加一个启动执行的shell文件
2.1:在/usr/local/opt/jenkins创建一个jenkinsBoot.sh文件,内容如下

#!/bin/sh
#默认是8080端口,这里指定为8888端口启动
jenkins --httpPort=8888

2.2:打开系统偏好设置->用户与群组->当前管理员->登录项->+/usr/local/opt/jenkins/jenkinsBoot.sh加入到这里来

常用默认参数:${WORKSPACE}${HOME}
查找正在运行的jenkins并关闭:

#查找正在运行的jenkins服务,查看jenkins.war那一行的端口号
ps -ef | grep jenkins
 501 82363 22132   0 12:33PM ttys000    0:00.00 grep jenkins
 501   926   855   0 Fri06PM ttys002    0:00.00 /bin/sh /usr/local/Cellar/jenkins/2.149/jenkinsBoot.sh
  501   xxxx   926   0 Fri06PM ttys002    6:58.53 /usr/bin/java -jar /usr/local/Cellar/jenkins/2.149/libexec/jenkins.war
#关闭jenkins.war那一行的端口号对应的服务
kill -int xxxx

应注意一个读者在配置jenkins环境时,配置keychain时的问题

说明:要jenkins打包iOS的ipa需要苹果系统支持,也就是说安装jenkins的机器是苹果系统或能用jenkins配置一台能访问的苹果系统的电脑作为master机器(本文以默认jenkins所在的机器就是苹果系统)

1、下载发布证书或从开发人员那里拿到导出的发布证书p12

  • 一个苹果证书怎么多次使用——导出p12文件
    双击安装证书后,jenkins所在的机器就有发布证书了
    login.keychain位置:
    /Users/管理员用户名/Library/keychains/login.keychain-db
    将login.keychain-db复制一份并命名为login.keyain
    描述文件位置:
    /Users/管理员用户名/Library/MobileDevice/Provisioning Profiles
    (不建议从这里找,因为上传这里的描述文件之后看到的标题也是UUID格式,所以最好单独上传从苹果开发网站下载的描述文件,或者把下面的描述证书拷贝出来改个名再上传)

2、将证书上传到jenkins里
将1中复制的login.keychain上传到这里,如下图所示,
identities:找keychain里这个发布证书的内容

《Jenkins一:iOS自动打包完整实践》 image.png
《Jenkins一:iOS自动打包完整实践》 image.png

【Mac】解决jenkins执行shell脚本等场景中遇见的权限不足问题
******以上解决权限问题的方式会有异常问题,不能这样做
实际的过程,以要使用git为例

#1、切换到jenkins用户下
#[mac终端切换用户](https://blog.csdn.net/doudou19930614/article/details/81410823)
su - Jenkins
#2、创建一个文件夹
mkdir xxx
#3、clone对应的git
git clone xxxxxx
#4、为了不用在每次push到GitHub上时都输入用户名密码
#[mac下git push避免每次都输入用户名和密码的配置](https://www.cnblogs.com/zhaoshunjie/p/6306927.html)
#5、如果有提示需要输入全局的账号和邮箱
# 设置提交代码时的用户信息
git config --global user.name "[name]"
git config --global user.email "[email address]"
#后续其他以jenkins账号操作都正常了
一、使用Jenkins自动打iOS安装包前提条件

1、Mac OS系统(我所使用的是MacBook Pro 和Mac mini)
2、Xcode
3、cocoapods(非必须,如果使用cocoapod管理第三方库才需要)
4、Java
5、sourcetree(git管理工具非必须,我主要用来看代码有没有问题)
6、cornerstone(svn管理工具非必须,我主要用来讲打包的ipa文件上传到SVN上,而不是通常的放到第三方服务器)
7、Jenkins(homebrew安装方式和安装包安装方式两种)

二、使用Jenkins安装包安装方式遇到的问题

1、提示

Failed to connect to repository : Command “git -c core.askpass=true ls-remote -h git@XXXXX:XXXX/XXXX.git HEAD” returned status code 128:

《Jenkins一:iOS自动打包完整实践》 image.png

原因:

由于使用Jenkins安装包安装,不能直接使用http的方式为git地址(why?),需要切换成ssh地址,ssh地址需要配置秘钥

解决办法:

配置ssh秘钥参考文章解决在Mac下搭建Jenkins+GitLab持续集成环境踩到的坑(一)

2、提示

Code Signing Error: No certificate for team ‘xxxxx’ matching ‘iPhone Distribution: xxxxx.’ found: Select a different signing certificate for CODE_SIGN_IDENTITY, a team that matches your selected certificate, or switch to automatic provisioning.

原因:

由于使用Jenkins安装包安装,在安装时创建了一个新的用户,默认放在钥匙串里的证书Jenkins这个用户访问不了

解决办法:

将钥匙串下的 登录里的相关证书拷贝到 钥匙串下的 系统里面

3、提示

Run custom shell script ‘[cp] embed pods frameworks ….. errSecInternalComponent.

/usr/bin/codesign –force –sign DC8791E5F036D9BAAFEC8E347093A20EB435BF0F –preserve-metadata=identifier,entitlements ‘/Users/Shared/Jenkins/Home/workspace/iOSDev/Release-iphoneos/xxx.app/Frameworks/AFNetworking.framework’
/Users/Shared/Jenkins/Home/workspace/iOSDev/Release-iphoneos/xxx.app/Frameworks/AFNetworking.framework: errSecInternalComponent

Command /bin/sh failed with exit code 1

** BUILD FAILED **

The following build commands failed:
PhaseScriptExecution [CP]\ Embed\ Pods\ Frameworks /Users/Shared/Jenkins/Library/Developer/Xcode/DerivedData/Lottery-dubolgbhbwxukkfsjmthrywdixge/Build/Intermediates.noindex/Lottery.build/Release-iphoneos/xxx.build/Script-79402CD10148DF169F37FF35.sh
(1 failure)
Build step ‘执行 shell’ marked build as failure
[OS X] restore keychains as defined in global configuration
[iOSDev] $ /usr/bin/security list-keychains -s /Users/Shared/Jenkins/Library/Keychains/login.keychain
Finished: FAILURE

《Jenkins一:iOS自动打包完整实践》 image.png

原因一:

看了一篇文章Run custom shell script ‘[cp] embed pods frameworks ….. errSecInternalComponent,按着里面的操作后并没解决问题,

解决办法:

看到–force –sign的字样猜测还是权限的问题,于是我把拷贝到钥匙串->系统 下的那个证书设置为 始终信任,顺利解决问题
如果还不可以则:
xcodebuild -workspace .....前添加一行解锁login.keychain的命令
security unlock-keychain -p 个人的账号登录密码 /Users/Shared/Jenkins/Library/Keychains/login.keychain

原因二:

在要用一个新appleID开发者账号创建一个新target时,电脑的login.kechain-db已经重新生成或者说修改了,但之前配置在Jenkins里的还是旧的,所以需要重新将login.keychain-db复制一份并重命名为login.kehchain上传到Jenkins里忘了在Jenkins配置时Keychains and Provisioning Profiles Management->Keychains
,同时将login.keychain-db、login.keychain拷贝到Jenkins的对应目录下,我的目录为:/Users/xxxx/Library/Keychains -> /Users/Shared/Jenkins/Library/Keychains
4、提示

xcrun: error: unable to find utility “PackageApplication”, not a developer tool or in PATH

原因:

新版的Xcode里没有想过的工具库

解决办法:

参考:xcrun: error: unable to find utility “PackageApplication”, not a developer tool or in PATH
顺便由于提示warning: PackageApplication is deprecated, use ``xcodebuild -exportArchive`` instead.,这个根据提示替换修改下就好了

5、提示

Build step ‘执行 shell’ marked build as failure
[OS X] restore keychains as defined in global configuration

原因:

Jenkins用户权限比较低,不能使用login.keychain

解决办法:

xcodebuild -workspace .....前添加一行解锁login.keychain的命令
security unlock-keychain -p 个人的账号登录密码 /Users/Shared/Jenkins/Library/Keychains/login.keychain

6、提示

=== BUILD TARGET xxxx OF PROJECT Lottery WITH CONFIGURATION Release ===

Check dependencies
Code Signing Error: “xxxx” requires a provisioning profile with the Push Notifications feature. Select a provisioning profile for the “Release” build configuration in the project editor.
Code Signing Error: Code signing is required for product type ‘Application’ in SDK ‘iOS 11.4’

** BUILD FAILED **

原因一:

该target的build setting配置不正确,之前是将Code Signing Identity统一配置成iOS Distribution了,这种方式在xcode里手动点击archive打包时没问题,但使用Jenkins自动打包就出报上面的错

《Jenkins一:iOS自动打包完整实践》 image.png

解决办法:

将报错的这个targetbuild setting设置为精确的描述证书,比如iPhone Distribution: xxxxx,如下图:

《Jenkins一:iOS自动打包完整实践》 image.png

另外之前查找资料时发现一些相关的资料,但没有解决我的问题
Jenkins+XCode9自动打包错误处理
最全Jenkins+SVN+iOS+cocoapods环境搭建及其错误汇总

7、提示

error: Unable to create ‘/Users/admin/Documents/svn/docs/xxxx.ipa’

原因:

这个路径是为了设置将打包出来的ipa安装包自动放到SVN路径下然后自动上传到SVN服务器,但还是由于打包Jenkins用的是一个独立的用户导致的权限问题

解决办法:

将/Users/admin/Documents/svn/docs/这个路径的每一个文件夹都设置为所有用户有读写权限

花了两个周构建几百次的血的教训,建议还是都用homebrew的方式安装少些权限问题

end
配置的参数:

《Jenkins一:iOS自动打包完整实践》 多个scheme的配置

《Jenkins一:iOS自动打包完整实践》 Debug/Release

《Jenkins一:iOS自动打包完整实践》 git分支和tag选择

《Jenkins一:iOS自动打包完整实践》 参数配置的使用页面

完整构建脚本:

# 工程名
APP_NAME="xxxx"


#***************动态配置部分*********************
# 不勾上自动管理证书
CODE_SIGN_DISTRIBUTION="iPhone Distribution: xxxxx"
# 代码文件夹名称,这是由于源码并不在git项目的根目录下
code_folder="xxxx"
#scheme#Info文件名称#保存ipa文件夹
platformParams=(${iOS_PLATFORM//\#/ })
# schema名称
SCHEMECA=${platformParams[0]}
#info.plist文件名称
INFO_FILENAME=${platformParams[1]}
# 输出到svn的文件夹名称
OUT_SVN_FOLDER=${platformParams[2]}
#*************************************
project_path=""
project_infoplist_path="./${code_folder}/Resource/platforms/${INFO_FILENAME}.plist"

#取版本号
bundleShortVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${project_infoplist_path}")

#取build值
bundleVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" "${project_infoplist_path}")
#取bundleId
bundleId=$(/usr/libexec/PlistBuddy -c "print CFBundleIdentifier" "${project_infoplist_path}")
DATE="$(date +%Y%m%d-%H%M%S)"
#唯一名称ipa、dsYM文件名
UNIQUE_NAME="${APP_NAME}_${SCHEMECA}_${iOS_Release}_V${bundleShortVersion}_${DATE}"
IPANAME="${UNIQUE_NAME}.ipa"
#根据需求定义输入ipa的文件夹位置
PARENT_FOLDER="$HOME/testJenkins/test/study/${OUT_SVN_FOLDER}"

if [ ! -d $PARENT_FOLDER  ];then
  mkdir -p $PARENT_FOLDER
else
  echo dir exist
fi
#要上传的ipa文件路径
IPA_PATH="$PARENT_FOLDER/${IPANAME}"
echo ${IPA_PATH}
echo "${IPA_PATH}">> text.txt

#下面2行是没有Cocopods的用法
#echo "=================clean================="
#xcodebuild -target "${TARGET_NAME}"  -configuration 'Release' clean
#
#echo "+++++++++++++++++build+++++++++++++++++"
#xcodebuild -target "${TARGET_NAME}" -sdk iphoneos -configuration 'Release'  CODE_SIGN_IDENTITY="${CODE_SIGN_DISTRIBUTION}" SYMROOT='$(PWD)'

#下面2行是集成有Cocopods的用法
echo "=================clean================="
xcodebuild -workspace "./${code_folder}/${APP_NAME}.xcworkspace" -scheme ${SCHEMECA}  -configuration "${iOS_Release}" clean


echo "+++++++++++++++++build+++++++++++++++++"
xcodebuild -workspace "./${code_folder}/${APP_NAME}.xcworkspace" -scheme ${SCHEMECA} -sdk iphoneos -configuration "${iOS_Release}" CODE_SIGN_IDENTITY="${CODE_SIGN_DISTRIBUTION}" SYMROOT='$(PWD)' -allowProvisioningUpdates



xcrun -sdk iphoneos PackageApplication "./${iOS_Release}-iphoneos/${SCHEMECA}.app" -o ${IPA_PATH}
#生成dsYM文件
dsymutil "./${iOS_Release}-iphoneos/${SCHEMECA}.app/${SCHEMECA}" -o "${PARENT_FOLDER}/${UNIQUE_NAME}.app.dSYM"
cd "${PARENT_FOLDER}"
if [ ! -d "${UNIQUE_NAME}.app.dSYM"  ];then
  echo "没有可以压缩的dsYM文件"
else
 #压缩dsYM文件
  zip -q -r -m -o "${UNIQUE_NAME}.app.dSYM.zip" "${UNIQUE_NAME}.app.dSYM"
  #上传dsYM文件到解析平台
  #curl -k "https://api.bugly.qq.com/openapi/file/upload/symbol?app_key=xxxxxx&app_id=xxxxxx" --form "api_version=1" --form "app_id=xxxxxx" --form "app_key=xxxxxx" --form "symbolType=2"  --form "bundleId=${bundleId}" --form "productVersion=${bundleShortVersion}" --form "channel=xxx" --form "fileName=${UNIQUE_NAME}.app.dSYM.zip" --form "file=@${UNIQUE_NAME}.app.dSYM.zip" --verbose
fi
# 将生成的ipa上传到SVN
#cd "/Users/tony/Documents/svn/docs/svn/"
#svn upgrade
#svn add ${IPA_PATH}
#svn commit -m "add test" ${IPA_PATH} 
三、使用homebrew安装方式遇到的问题

自动打包实践:
一、Jenkins自动打包管理,参考教程如下:
手把手教你利用Jenkins持续集成iOS项目
iOS开发-自动化打包Jenkins集成
jenkins – iOS自动化打包分发
iOS自动化打包实战(Jenkins)
Jenkins安装、配置、构建、脚本、配置邮箱、上传fir
Mac下Jenkins+SVN(Git)+Xcode搭建持续构建环境
Mac jenkins 开机启动设置

选择指定的tag自动脚本打包
Confluence-Documention

二、遇到问题

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.Build《Jenkins一:iOS自动打包完整实践》AbstractBuildExecution.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`目录下。

xcrun: error: unable to find utility "PackageApplication", not a developer tool or in PATH
后面根据对比发现新版的Xcode少了这个PackageApplication
先去找个旧版的Xcode里面copy一份过来
放到下面这个目录:
/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

最后附上PackageApplication下载地址:
https://pan.baidu.com/s/1jHJF2Lo

mac系统命令行curl详解

遇到问题

Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_XXXX", referenced from:
      objc-class-ref in XXXX.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

** BUILD FAILED **

原因:
当前target没有把XXXX这个文件的.m文件没有勾选到Target MemberShip
这种问题特别容易出现在一个项目有多个target的时候
如下:

《Jenkins一:iOS自动打包完整实践》 tmp633006ba.png

解决办法:

将提示的文件勾选到
Target MemberShip

问题:

Warning: unable to build chain to self-signed root for signer "iPhone Distribution: "/Users/Shared/Jenkins/Home/workspace/xxx/Release-iphoneos/xxx.app/Frameworks/AFNetworking.framework: errSecInternalComponent
Command /bin/sh failed with exit code 1

** BUILD FAILED ** 

原因:
忘记把新添加的p12证书对应的Identity添加到Jenkins->系统管理->Keychains and Provisioning Profiles Management->Add code Signing Identity里,

《Jenkins一:iOS自动打包完整实践》 tmp2cbdfdaa.png

Mac 开启Apache服务器及PHP环境
Mac下配置Apache服务器
搭建企业账号ipa包分发下载平台
Mac中开启Apache的Https功能

    原文作者:独孤流
    原文地址: https://www.jianshu.com/p/d6fdd13a7201
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞