macOS 上使用 Jenkins 实现 iOS 自动化打包

前言:

实际实践过程中,发现通过 .pkg 包来安装的 Jenkins 存在各种各样的问题,比如 SSH 配置问题,Git Clone 超时问题,Provisioning Profile 读取权限问题(因为Jenkins的工作目录会建立在/Users/Shared/Jenkins)等,很难去解决!而通过 Homebrew 命令来安装的 Jenkins 就没有这些问题,强烈推荐在 macOS 上采用 Homebrew 命令来安装 Jenkins,而不是通过官方网站上下载的 .pkg 包来安装!

1、环境配置:

1、Mac OS 10.14.4

2、Xcode10.2

3、Homebrew安装:https://juejin.im/post/5a6d969bf265da3e2366b853

4、JavaSDK安装:下载Java的JDK


2、Jenkins安装:

2.1、通过Homebrew安装Jenkins,终端中输入:

 brew install jenkins


2.2、然后执行war包(注意下面的命令需要自己更换对应的版本号),终端中输入:

java -jar /usr/local/Cellar/jenkins/版本号/libexec/jenkins.war --httpPort=8080

出现下面界面说明jenkins已经启动成功了

3.3、在浏览器中输入localhost:8080出现下面界面:

.jenkins是隐藏文件,可以按住shift+command+. 查看mac上的隐藏文件,找到initialAdminPassword复制其中的密码,然后按住shift+command+. 隐藏mac上的隐藏文件。


3.4、安装推荐的插件

3.5、创建管理员用户

3.6、进入到以下页面,就表明 macOS 上 Jenkins 的安装与配置成功!


3.7、安装依赖插件

如果是GitLab来管理源代码,Jenkins本身并没有自带GitLab插件,所以我们需要依次选择 系统管理->管理插件,在“可选插件”中选中“GitLab Plugin”和“Gitlab Hook Plugin”这两项,然后安装。

如果你想用Xcode插件作为配置打包任务可以下载 xcode integration插件,下载方式也是系统管理->管理插件,在“可选插件”中搜索下载。不过我还是偏向与脚本的方式,因为脚本虽然麻烦些但是更加灵活。比如我们打包的命名方式,打完包的存放文件路径等都可以通过脚本做配置。

如果你工程使用了cocoapods,需要下载CocoaPods Jenkins Integration插件。

自动打包的job任务配置也需要添加钥匙串和描述文件,这时需要安装Keychains and Provisioning Profiles Management插件,安装后在Jenkins首页点击系统管理会找到这个插件,点击进去。

3.8、为Jenkins配置Keychains and Provisioning Profiles Management

去 macOS 以下地址:/Users/{username}/Library/Keychains找到 login.keychain 文件,有的时候你可能看到的是 login.keychain-db 文件,没关系,将 login.keychain-db拷贝桌面一份并将文件修改成 login.keychain。然后通过 Upload Keychain or Provisioning Profile File -> Choose File -> Upload 上传:


上传好了之后,就会出现以下配置:


Password 一栏就填写当前 macOS 用户的登录密码。Code Signing Identity 这个地方我们填写相关证书的标识符,我目前电脑上配置了一个调试证书和一个 Ad Hoc 发布证书,当然后面你还需要一个 App Store 发布证书,这个操作流程都是一样的,后面自己处理就好。我们去到 Keychain Access -> login,找到你的 iPhone Developer 和 iPhone Distribution 证书,选择其中一个,然后复制标识符,填写到此位置,保存即可!

说明:这个操作步骤,可以理解为 Jenkins 有权以你这台电脑(login.keychain 文件)来获取调试和发布 iOS 应用的证书(证书标识符)。

然后我们拿到调试证书和 Ad Hoc 证书对应的 .mobileprovision 文件,建议去 Apple 开发者后台去下载,也可以去到 /Users/{username}/Library/MobileDevice/Provisioning Profiles 这个路径下去找,但是如果你的配置文件太多的话,你很难分辨是哪个文件,而且这个地方的名称都是 UUID 值标记的。我们拿到对应的 .mobileprovision 文件,同样的位置点击上传,出现以下页面:

会自动识别出 .mobileprovision 文件的 UUID 值。对于 Provisioning Profiles Directory Path,根据大家 macOS 当前登录的用户名的不同,我们填写:/Users/{username}/Library/MobileDevice/Provisioning Profiles。

说明:这个操作步骤,可以理解为 Jenkins 可以读取你位于 /Users/{username}/Library/MobileDevice/Provisioning Profiles 目录下的配置文件,同时知道应该读取那个对应的配置文件(识别 UUID 值)。

保存好了之后,整个 iOS 工程项目相关的证书和配置文件都配置好了。


3.9、为Jenkins配置Credentials

为了方便访问远程的 Git 仓库,我们需要配置用于登录验证的信息。Jenkins -> Manage Jenkins -> Configure Credentials -> Credentials -> System -> Global Credentials -> Add Credentials。

说明:我们可以通过两种方式来访问远程 Git 仓库,一个是用户名 + 密码的形式,另外一个是 SSH 形式,关于 SSH 的公钥、私钥的制作与关联使用,请参见《在 Mac OS X 上关联 GitHub》。我们这里就追加两种方式的登录信息,以防止一种出现问题,另外一种还可以使用。

用户名和密码形式,Kind 选择:Username with password。Scope 选择 Global,Username 为你登录 GitLab 的用户名,Password 为你登录 GitLab 的密码。ID 默认不用填写,Description 我们这里写一个可以区分当前登录信息的字符串即可。

SSH 形式,Kind 选择:SSH Username with private key,Scope 还是选择 Global,Username 还是 GitLab 登录的用户名,Private Key 我们去到通过我以上文章方式生成的用于 SSH 的公钥和私钥文件夹:/Users/{username}/.ssh:打开 id_rsa,然后复制内容到 Key 里面,Description 还是和上面一样,写一个可以区分当前登录信息的字符串即可。

保存好这两种登录方式的信息之后,我们在列表中可以看到以下内容,说明保存成功了!

3.10、关联一个远程 GitLab 的 iOS 工程项目

Jenkins -> New Item -> 输入名称 -> Freestyle project -> OK

3.11、配置 iOS 工程项目

Jenkins -> ios-bluethooth-vioce-assistant -> Configure,其中 ios-bluethooth-vioce-assistant 为新建的Jenkins工程名称

相关的配置如下:

(1)Description:项目的一个简要的描述:

        我们的项目是:iOS 蓝牙语音助手。

(2)Source Code Management:项目代码版本管理方式:

        我们的项目是使用 Git 托管在 GitLab 上面,所以我们选择 Git。Repository URL 我们填写 Git 仓库的地址。请注意:如果你选择的是 http:// 打头的 Git 仓库地址,那么 Credentials 就要选择:用户名和密码形式。如果你选择的是 git@ 打头的 Git 仓库地址,那么 Credentials 就要选择:SSH 形式。需要 Build 的分支我们就默认 master。其他参数默认即可,说明:你选择 git@ SSH 形式访问远程 Git 仓库,有可能会出现以下提示:

        你可以参考这篇文章尝试解决:https://www.jianshu.com/p/ed0edb93e234,我个人由于没有解决掉这个问题,就采用 http:// 方式访问了。另外我猜测这个是由于通过 .pkg 包安装的方式来安装的,会生成一个共享的 Jenkins 账户,而通过 Brew 命令来安装的则是会安装到当前用户目录底下,账户之间的权限访问问题,比如你配置的是当前用户底下的 .ssh 信息,但是实际上 Jenkins 尝试去 /Users/Shared/Jenkins 底下获取相关的 .ssh 信息。

(3)Build Triggers构建触发器:

        就是希望以什么样的机制来触发项目的构建。由于是独立的单个项目,一般采用的方式有两种:Build periodically,无论远程仓库代码是否变化,周期性构建。Poll SCM,当远程仓库代码有变化时,才进行周期性构建。我们目前采用 Poll SCM 形式。大家要注意的就是如何去写这个配置参数,在配置这个参数之前,我们需要了解一个非常重要的概念:Crontab,还有一个非常好的针对 Crontab 的工具网站。了解清楚之后,我们就可以先配置一个测试的 Crontab:*/5 * * * *。表示每五分钟构建一次,我上图中填写的是:H/5 * * * *,两个意思是一样的。当你填写 */5 * * * * 的时候,Jenkins 会提醒你写成:H/5 * * * * 形式更好。

(4)Build Environment构建环境:

        这个地方主要配置的是针对 iOS 项目的证书和配置文件。勾选上:Keychains and Code Signing Identities,这个时候出现 login.keychain 的信息。但是可能会出现 Code Signing Identity 没有可以选择的情况,你需要先点击保存一下,让相关的数据加载,不然你是无法选择这个 Code Signing Identity。这是一个小坑!我们选择 iPhone Developer 的证书,那么底下勾选上:Mobile Provisioning Profiles 之后,选择的配置文件是要和 iPhone Developer 证书对应。当你选择 iPhone Distribution 证书之后,Mobile Provisioning Profiles 要选择和 iPhone Distribution 证书对应的配置文件。

(5)Build构建:

        我们选择 Execute shell,执行一段 Shell 命令。我们这边执行 Shell 命令,需要注意针对 iOS 项目是否有引入 CocoaPods 而不同。有经验的 iOS 开发者都知道,如果你引入了 CocoaPods,那么你需要通过 .xcworkspace 文件载入工程,而不是 . xcodeproj 文件载入工程。

(6)引入了 CocoaPods 的项目编译的 Shell 命令示例:

#bin/bsah - l

export LANG=en_US.UTF-8

export LANGUAGE=en_US.UTF-8

export LC_ALL=en_US.UTF-8

cd ~/.jenkins/workspace/ios-bluethooth-vioce-assistant

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

# 项目名称:iOS 项目工程中的名称

PROJECT_NAME="VoiceAssistant"

# Scheme 名称:iOS 项目工程中 Scheme 名称

SCHEME_NAME="VoiceAssistant"

# Info.Plist 文件的路径,请注意有时候 Info.Plist 的文件名和路径可能不同

SCHEME_INFO_PLIST_PATH="./${PROJECT_NAME}/Info.plist"

# 从 Info.Plist 文件中读取版本号

VERSION=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${SCHEME_INFO_PLIST_PATH}")

# 从 Info.Plist 文件中读取构建号

BUILD=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" "${SCHEME_INFO_PLIST_PATH}")

# 获取当前日期

DATE="$(date +%Y%m%d%H%M)"

# 编译之后的 .ipa 文件名称

IPA_NAME="${SCHEME_NAME}_v${VERSION}_${DATE}"

# 编译之后的 .xcarchive 文件名称

ARCHIVE_NAME="${SCHEME_NAME}_v${VERSION}_${DATE}"

# 编译之后的 .ipa 文件导出路径

IPA_EXPORT_PATH="/Users/ifeegoo/workspace/jenkins/${PROJECT_NAME}/${SCHEME_NAME}/${VERSION}/"

# 存档打包导出 .ipa 文件

xcodebuild archive -scheme ${SCHEME_NAME} -archivePath ${IPA_EXPORT_PATH}${IPA_NAME}.xcarchive -workspace ${PROJECT_NAME}.xcworkspace

xcodebuild -exportArchive -archivePath ${IPA_EXPORT_PATH}${ARCHIVE_NAME}.xcarchive -exportPath ${IPA_EXPORT_PATH}${IPA_NAME}.ipa -exportOptionsPlist /Users/ifeegoo/workspace/jenkins/${PROJECT_NAME}/${SCHEME_NAME}/ExportOptions.plist

(7)未引入 CocoaPods 的项目编译的 Shell 命令示例:

# 项目名称:iOS 项目工程中的名称

PROJECT_NAME="VoiceAssistant"

# Scheme 名称:iOS 项目工程中 Scheme 名称

SCHEME_NAME="VoiceAssistant"

# Info.Plist 文件的路径,请注意有时候 Info.Plist 的文件名和路径可能不同

SCHEME_INFO_PLIST_PATH="./${PROJECT_NAME}/Info.plist"

# 从 Info.Plist 文件中读取版本号

VERSION=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${SCHEME_INFO_PLIST_PATH}")

# 从 Info.Plist 文件中读取构建号

BUILD=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" "${SCHEME_INFO_PLIST_PATH}")

# 获取当前日期

DATE="$(date +%Y%m%d)"

# 编译之后的 .ipa 文件名称

IPA_NAME="${SCHEME_NAME}_v${VERSION}_${DATE}"

# 编译之后的 .xcarchive 文件名称

ARCHIVE_NAME="${SCHEME_NAME}_v${VERSION}_${DATE}"

# 编译之后的 .ipa 文件导出路径

IPA_EXPORT_PATH="/Users/ifeegoo/workspace/jenkins/${PROJECT_NAME}/${SCHEME_NAME}/${VERSION}/"

# 存档打包导出 .ipa 文件

xcodebuild archive -scheme ${SCHEME_NAME} -archivePath ${IPA_EXPORT_PATH}${IPA_NAME}.xcarchive -workspace ${PROJECT_NAME}.xcodeproj

xcodebuild -exportArchive -archivePath ${IPA_EXPORT_PATH}${ARCHIVE_NAME}.xcarchive -exportPath ${IPA_EXPORT_PATH}${IPA_NAME}.ipa -exportOptionsPlist /Users/ifeegoo/workspace/jenkins/${PROJECT_NAME}/${SCHEME_NAME}/ExportOptions.plis


当然,执行以上命令的前提是当前 macOS 已经安装了 Xcode!

你一定注意到了,上面针对于是否引入 CocoaPods 不同的 Shell 命令,唯一的区别就是在最后面载入不同格式的工程文件。另外你也可能注意到了,编译的 Shell 命令里面有一个 ExportOptions.plist 的文件,有些疑惑这个文件是哪里来的,第一次的时候,我们可以通过Xcode打包导出对应的 Development/Ad Hoc/App Store 包,该包里面包含这几个文件:DistributionSummary.plist、ExportOptions.plist、VoiceAssistant.ipa、Packaging.log,如果你想要 Build Development 的包,你就需要通过 Development 证书和对应的配置文件来导出包,获得这个文件。如果你想要 Build Ad Hoc 的包,你就需要通过 Ad Hoc 证书和对应的配置文件来导出包,获得这个文件。ExportOptions.plist 文件内容示例:

以上是通过 Xcode 导出的 Ad Hoc 包的 ExportOptions.plist 文件,这个记录当前工程导出的相关配置。注意:我们将以上配置文件中第六行修改成 false,因为 compileBitcode 默认的 true 可能导致编译失败。然后将此文件放置到:/Users/ifeegoo/workspace/jenkins/${PROJECT_NAME}/ 目录底下:/Users/ifeegoo/workspace/jenkins/${PROJECT_NAME}/ExportOptions.plist。

3.12、至此,所有的准备工作都已经就绪,去到工程页面,点击左侧的:Build Now,第一次通过手动触发构建。

左下角显示第一次构建的进度,最后变成了红色。很显然构建失败了,这个也很正常,一次成功的例子在开发中是不常见的。不要慌,点击编译失败的任务,弹出框选择:Console Output


很明显我们可以看到,上面提示 timeout=10,针对这种超时的提醒,对 Git 有经验的开发者来说,已经很熟悉了。意思就是 10 分钟的 Git Clone 动作已经超时了,一般会出现在初次 Clone 时网络不太好或者远程 Git 仓库过大造成的,我们现在要做的就是调大超时时间:

还是去到当前项目的配置页面:Source Code Management -> Additional Behaviours -> Advanced clone behaviours -> Timeout (In Minutes) for clone and fetch operations -> 设置成 60 (分钟),根据自己的网络实际情况和远程仓库的大小。保存之后,再次手动构建一下。

当你发现时间设置成 120 分钟这种超长时间,然后 Git Clone 还是一直卡在那里,同时我们也尝试了去 Clone 一个非常小的项目,确定还是同样的情况,这个就说明,不是超时时间设置的问题,网上有人说可能是因为防火墙设置,导致 Git Clone 出现问题,推荐使用 SSH 方式 Clone 项目,但是前面说了,通过 .pkg 文件安装的 Jenkins,是挂在 Jenkins 用户底下的,需要单独针对这个 Jenkins 来配置 SSH 信息,我也没有解决这个问题,所以,当你遇到这个问题之后,推荐采用 Brew 命令来安装 Jenkins,而且在实际的时间过程中,你会发现通过 .pkg 包安装的 Jenkins,要解决很多问题,有的问题还不好解决,而通过 Brew 命令安装的 Jenkins,相对来说,出问题的情况少些!!!

当你发现通过 .pkg 安装的 Jenkins 有太多问题解决不了的话,先卸载,然后通过 Homebrew 命令行来安装 Jenkins!

通过 Homebrew 命令来安装 Jenkins 之后,前面的配置步骤都是一样的!我们同样尝试 SSH 形式访问远程 Git 仓库。再次 Build,发现没有 Git Clone 超时的问题!

如果遇到git lfs的错误,说明需要安装 Git LFS,终端执行brew install git-lfs

4、后续问题参考文章:

https://www.ifeegoo.com/using-jenkins-to-set-up-the-continuous-integration-environment-of-android-and-ios-on-macos.html

你可能感兴趣的:(macOS 上使用 Jenkins 实现 iOS 自动化打包)