一切得从上个版本的打包发布说起,时失败了

2019-09-14 09:50栏目:大奖888官网登录
TAG:

最近在使用CI平台打包时突然失败了,查看日志后发现是在exportArchive时失败了。之前一直都是好好地,升级了Xcode之后突然就不行了,提示如下信息:

一切得从上个版本的打包发布说起。

3个问题

  1. 安装优化软件环境nginx,lvs <== 运维工程师
  2. 程序代码(不断更新)<==开发工程师,开发+运维
  3. 配置变更(不断变更)<==运维工程师
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.}

开发中本人负责了iOS包的版本发布工作。iOS打包:不就是选一下证书,再在Xcode上点几下按钮,IDE全都给你设置好流程了,有必要这么麻烦吗?

SVN介绍

SVN是Subversion的简称,是一个开放源代码的版本控制系统,主要用来管理代码的,就跟
文件服务器和ftp服务器一样,但是,它能监控文件的修改并记录每次的变化,必要时可以还原
到之前的某一个修改,采用集中式的代码管理。
开始新一天的工作:

图片 1

0 、在中央库上创建或从主干上复制一个分支
1、从服务器下载项目组最新代码(check out)。
2、进入自己的分支,进行工作,每隔一个小时向服务器自己的分支提交一次代码(很多人都有这个习惯。因为有时候自己对代码改来改去,最后又想还原到前一个小时的版本,或者看看前一个小时自己修改了哪些代码,就需要这样做了)。
3、下班时间快到了,把自己的分支合并到服务器主分支上,一天的工作完成,并反映给服务器。
这就是经典的svn工作流程。
缺点:和中央库的话交互需要网络(一般局域网不用担心),数据版本记录需要备份

查阅资料后发现,在Xcode9下,xcodebuild需要配置更多的信息才能导出ipa,最主要的一个就是provisioningProfiles。具体的操作步骤如下。

诚然,如果只是打包,在不考虑团队协同合作、打包效率、重复工作量的前提下,使用Xcode自带的打包方式当然是没问题的。但实际开发中,每次打包大概包含以下流程:拉取最新代码编译通过设置打包环境(开发、测试、生产等)导出IPA包上传IPA包(App Store或者企业包上传至指定服务器)

SVN和git的区别

svn是一个集中式的版本控制系统。
git是一个分布式的版本控制系统,本地有一个和远程一样的库(这2个库逻辑上是等价的),支持离线开发,而且git也可以做这种集中式的版本控制,只是操作上可能没有svn快捷而已,每次提交时都让本地库和远程库进行同步的话就是集中式的版本控制。

  1. 使用Xcode Archive一个新的版本

    图片 21.png

  2. Organizer中找到刚刚Archive出来的版本,选择Export

    图片 32.png

  3. 选择你要导出的ipa类型,如果你需要不同版本,可以重复该流程,就可以得到其他类型所需要的信息了。

    图片 43.png

  4. 导出ipa到目录

    图片 54.png

  5. 最终导出的目录下会有4个文件,除了ipa文件还有一个ExportOptions.plist文件,这个文件就是我们使用xcodebuild -exportArchive命令时,-exportOptionsPlist参数需要指定的plist文件。

    图片 65.png

可以看出,其中的很多步骤都是机械重复的,特别当进入测试验收阶段,有时每修复几个bug就要重新打包发布测试,比如上个版本的时候新功能主要是队友在开发,测试到最后频繁地让我打包发布,不停地打断我的工作去重复机械的事情,这简直就是在浪费人生啊!!!

SVN的运行模式以及访问方式

服务器端访问主要3种:

  1. 单独的服务器访问
  2. 借助apach的Http访问
  3. 本地直接访问
    本人用的是VirtualSVN,下载地址:
    https://www.visualsvn.com/server/download/

客户端访问方式:一般是通过安装客户端软件
下载地址:https://tortoisesvn.net/downloads.html

我们用这个新的plist文件就可以了。如果你需要打其他类型的ipa,可以重复上述步骤,在第三步重新选择即可。你也可以按照刚才导出的plist自己修改。

虽然之前为了打包方便,我已经整理了一份脚本打包的流程(传送门:Shell脚本——Xcode脚本打包),但还是不够方便快捷,乘着新版发布后的空档期,撸出 一键打包发布系统 ,功能包括:自动拉取Git最新代码自动选择签名证书并打包导出ipa文件Git自动同步代码自动上传ipa包(我们是企业包,上传至自己的服务器,这一步是可选的)

SVN档案库数据格式(类似文件系统):

BDB(事务安全型表类型)
FSFS(一种不需要数据库的存储系统)
VDFS ( 多仓库分布式的文件存储系统)

Replication links between a master repository and its slaves are maintained by a service named VisualSVN Distributed File System Service (also referred to as 'VDFS service') running on all the partner servers.
The VDFS service is disabled by default. You are required to enable this service on all the replication partners to get started with the repository replication functionality.
简单来说这个是VisualSVN基于FSFS文件系统格式扩展的。也就是说,分布式版本管理DVCS兴起之后,大家发现多个仓库的好处了,开始给SVN增加这个功能。至于FSFS本身是SVN在2004年开始替换Berkeley DB的,是基于文件系统概念设计的,而不是基于数据库的概念,采用目录树的结构去做transaction,可以在目录外创建完一次提交,然后把完成的提交一次性移动进版本历史。另外,根据他们的数据结构,对Berkeley DB的append操作是O(N^2)的,而FSFS是O(N)的。如果你不是太在乎私有格式,也需要多仓库同步,可以用VDFS,官方文档说了VDFS默认的service是没有启动的,所以对你日常应该没有影响。如果不喜欢私有格式,就用FSFS就好。


新的plist中有如下一些选项,你也可以参照修改。

一键打包

项目链接地址

SVN服务端

SVN服务端的配置有多种方案:可以安装Svnserve服务,也可以安装Apache服务,但最简单的莫过于使用VisualSVN Server这种方式.(https://www.visualsvn.com/server/download/)

  1. 开始安装点next
![](https://upload-images.jianshu.io/upload_images/7230921-3b5a5bea03b65236.png)

图片.png
  1. 同意一下协议
![](https://upload-images.jianshu.io/upload_images/7230921-97dd8ceb28308e79.png)

图片.png
  1. 选择服务器版和管理源工具
![](https://upload-images.jianshu.io/upload_images/7230921-4a734378953f8f1b.png)

图片.png
  1. 这里选标准版的,专业版要密钥的,但是标准版好像不支持分布式版本仓库)
![](https://upload-images.jianshu.io/upload_images/7230921-4a55bdf7c4e8708f.png)

图片.png
  1. 自定义安装目录以及仓库位置
![](https://upload-images.jianshu.io/upload_images/7230921-95d77d865f8dec5e.png)

图片.png

版本库:版本库就是存放文件、代码及版本信息的地方。服务器针对某一项目创建版本库后,客户端才能访问该项目的文件、代码。因此在服务器上创建、删除版本库是十分重要的工作。

  1. 点“next”开始正式安装,之后启动可以看到如下界面
![](https://upload-images.jianshu.io/upload_images/7230921-e1b18f54d66abee7.png)

图片.png

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>compileBitcode</key> <false/> <key>method</key> <string>ad-hoc</string> <key>provisioningProfiles</key> <dict> <key>com.tsing.calculate</key> <string>calculate_adhoc</string> </dict> <key>signingCertificate</key> <string>iPhone Distribution</string> <key>signingStyle</key> <string>manual</string> <key>stripSwiftSymbols</key> <true/> <key>teamID</key> <string>CL32FD34</string> <key>thinning</key> <string>&lt;none&gt;</string></dict></plist>
一键打包资源(ArchiveSource)简介:
  • Code文件夹下是打包应用程序源代码
  • Source文件夹下是打包资源

Source文件夹:

  1. Archive.app
  2. ExportOptions.plist(导出ipa包配置文件)

服务器端的使用

  1. 启动服务器端:安装成功后点windows的“开始”会看到如下图标,点进去就是。
![](https://upload-images.jianshu.io/upload_images/7230921-25e2549ad6255bfe.png)

图片.png

![](https://upload-images.jianshu.io/upload_images/7230921-e1b18f54d66abee7.png)

图片.png
  1. 创建版本库
    a. 这里提供了2种格式的仓库,一种是普通的FSFS,一种是VDFS(这个格式的目前只有专业版支持)进入管理界面,选中Repositories,右键create New repositorie...

图片 7

图片.png

图片 8

图片.png

b. 创建输入仓库名称后

图片 9

图片.png

c. 提示选择Repository Structs,选择single-project repository

图片 10

图片.png

d. 然后会提示配置仓库权限,这里选择customize permissions自定义我们的权限,

图片 11

图片.png

e. 最后点击finish完成创建。


  1. 删除版本库
    选中需要删除的仓库,右键删除就是。注意:删除版本库是无法撤销的,请谨慎处理
  2. 用户管理
    a. 创建用户
    选中Users,右键选择create User ...,按要求输入用户名和密码便可。

    图片 12

b. 编辑用户  
选中用户,右键便会看到对应的功能按钮。

图片 13

图片.png

例如:修改用户密码

图片 14

图片.png

  1. 组管理
    组管理和用户管理一样的,这里就不赘述了。

  2. 权限管理
    a. 权限一般是作为仓库的一部分功能处理的,所以,当你想给一个用户或者一个组分配权限的时候,你需要直接右键选中你需要分配去权限的仓库,然后选中properties..单击出现如下面板,有什么想配置的权限直接使用面板就是。点击Add可以便可以进行权限配置。

图片 15

图片 16

图片 17

图片.png

b. 自己选择是给所有人分配,还是某个组还是某个用户,分配完成后点应用便可。

图片 18

图片 19


图片 20扫码关注公众号

一键打包步骤:
  1. 进入Source文件夹,启动 Archive 应用程序
  2. 选择 .xcodeproj 或 .xcworkspace 文件
  3. 修改打包版本号

启动Xcode,手动选择签名证书(如果默认签名失败的话)

  1. 点击打包

SVN客户端的使用

SVN客户端为TortoiseSVN,安装之后就可以访问服务器端的版本库。读取项目文件、代码,还可以将修改后的文件、代码上传到服务器上,与项目组成员共同维护项目文件、代码。

  • 3.1 安装TortoiseSVN
    下载地址:https://tortoisesvn.net/downloads.html
    自己根据需要选择下载的版本

    图片 21

运行安装程序,如:TortoiseSVN-1.9.7.27907-x64-svn-1.9.7.msi。其安装步骤比较简单,自己直接安装。
安装成功的话在桌面上右键会出现如下界面  

![](https://upload-images.jianshu.io/upload_images/7230921-3eae5a1d97ea40a7.png)
  • 3.2 Import(导入)
    导入、导出是以服务器上的版本库为中心的。导入就是将项目文档写入版本库。Windows资源管理器中,鼠标右键单击要导入的文件夹。在弹出菜单中,单击【TortoiseSVN】下的【Import...】菜单项。

  • 3.2.1. 首先选中你需要导入的项目文件夹,右键按如下操作点击便可。

![](https://upload-images.jianshu.io/upload_images/7230921-278f1ea68419d221.png)
  • 3.2.2. 进入导入界面,书写导入日志。
![](https://upload-images.jianshu.io/upload_images/7230921-b8333037417006d7.png)
  • 3.2.3. 进入SVN认证界面。输入用户名和密码后,单击"OK"按钮完成认证。注意用户名和密码都是区分大小写的。如果觉得每次都要输入用户名和密码比较繁琐,请勾中"Save authentication"复选框。下次SVN会自动完成认证。
    SVN完成认证后,会将选定的文件夹内容导入到版本库中。
    需要说明的是
    1、一般只是在项目开始的时候才会用到导入功能;
    2、如果待导入的文件或文件夹在版本库内已经存在,则导入会失败。也就是说不能重复导入;
    3、目录问题。一次只能导入一个文件夹。假如导入的文件夹为Folder。导入到版本库时不会有Folder这个文件夹;

  • 3.3.4 导入成功后会看到如下界面

![](https://upload-images.jianshu.io/upload_images/7230921-c5b474f14a7dd66a.png)
  • 3.3 Export(导出)
    导出就是将项目文档从版本库中提取出来。Windows资源管理器中,单击鼠标右键。在弹出菜单中,单击【TortoiseSVN】下的【Export...】菜单项。SVN显示如下。

图片 22

在URL of repository下输入版本库的地址。在Export directory下输入本机存放目录。导出深度有四个选项,分别为:
Fully recursive(深度递归):导出指定目录下的文件、各级子目录以及各级子目录下的文件。也就是全部导出。默认就是选择该项;
Immediate children, including folders(直接子节点,包含文件夹):导出指定目录下的文件和一级子目录,但不导出一级子目录内的文件;
Only file children(仅文件子节点):只导出指定目录下的文件,不导出一级子目录及一级子目录内的文件。
Only this item(仅此项):只导出指定目录。导出后就是一个空目录,没有什么实际意义。
Revision是版本的意思。项目组里任何一人修改了文档,上传到服务器上后都会形成一个新的版本,也就会有一个新的版本号。HEAD revision 表示最新的版本,也可以输入指定的版本号。
配置好后,单击"OK"按钮。
因为要和服务器通讯,因此与导入一样,可能需要进行用户认证(我这里的版本不需要)。

  • 3.4 Checkout(检出)
    Windows资源管理器中,单击鼠标右键。在弹出菜单中,单击【SVN Checkout...】菜单项。剩下的步骤与导出大致相同。
![](https://upload-images.jianshu.io/upload_images/7230921-d74e6806620c3ac5.png)
  • 3.4.1 导出和检出的区别
    既然有导出功能了,还要检出做什么呢?因为与导出不同,检出的文档有两份。一份是用户能看到、修改的文档,被称之为Working Copy,以下简称Copy版;另一份是被隐藏在.SVN文件夹下的文档,被称之为Working BASE,以下简称BASE版。假如有Copy版的文件1.txt,则其对应的BASE版文件为.svntext-base1.txt.svn-base。1.txt有两份,被称之为versioned文件。如果用户将2.txt复制到检出目录,则2.txt只有一份,被称之为non-versioned文件。
    当用户修改了Copy版的文档,上传到服务器版本库的时候。SVN会比较Copy版和BASE版有什么不同之处,然后只将用户的修改信息上传至服务器。
    当用户修改了Copy版的文档,该文档的图标会被改变。这也是SVN比较Copy版和BASE版的结果。
    versioned文件有两个版本,因此对它的删除、重命名、移动必须通过SVN来完成,下面几节将介绍这些操作。
  • 3.5 删除文件
    资源管理器中,鼠标右键单击待删除的文件,单击弹出菜单的【TortoiseSVN】点击
    【Repo-Browser】浏览仓库,选择文件右键点击【Delete】菜单项即可完成删除操作。

图片 23

图片 24

  • 3.6 重命名文件
    同删除文件差不多的操作,只是最后选择的时【Rename】而已。
  • 3.7 增加文件
    检出目录里的non-versioned文件可以增加至BASE,变成versioned文件。
    资源管理器中,选中若干项文件或文件夹。鼠标右键单击某一选中项。单击弹出菜单的【TortoiseSVN】【Add...】菜单项。SVN显示如下界面。单击"OK"按钮即可完成增加操作。

图片 25

需要注意的是这个增加只是增加到本机的BASE,而不是直接增加到服务器的版本库中

  • 3.8 移动文件
    资源管理器中,选中若干项文件或文件夹。移动鼠标至某一选中项,按下鼠标右键不放,移动鼠标至目标文件夹再放开鼠标右键。
  • 3.9 Commit(提交)
    当用户对检出的文档做了修改后,需要将修改信息上传到服务器的版本库里,以便项目组的其它成员共享。这个上传的过程就是提交。
    Windows资源管理器中,鼠标右键单击要提交的文件夹。在弹出菜单中,单击【SVN Commit...】菜单项。SVN显示如下界面:

图片 26

图片 27

message中写入你对项目文档都做了哪些修改。一定要养成填写该项的好习惯,方便项目组其他成员清楚项目都发生了哪些变化。
Changes made下的列表列出了哪些文件被改变了。改变的文件分为两大类:一类是在Copy版中存在,但在BASE版中不存在的文件,即non-versioned文件;另一类是在BASE版中存在的文件,即versioned文件,其Text status有可能是modified(修改)、added(增加)、deleted(删除)……
列表中勾中要提交的文件,单击"OK"按钮即可完成提交操作。提交过程中可能会产生冲突,其解决方法请参考"冲突"这一节。

  • 3.9.1 non-versioned文件
    对于non-versioned文件,SVN默认不提交至服务器。下次提交的时候,如果希望某个non-versioned文件不再出现在列表中,其方法就是忽略。
    提交界面(图3.11)中,鼠标右键单击一个non-versioned文件夹,弹出菜单中有【Add to ignore list】。它有两个选项,一个是文件夹本身,另一个是。前者表示忽略该文件夹,下次提交时该文件夹将不再显示在列表里。后者表示忽略所有non-versioned文件,下次提交时所有的non-versioned文件将不再显示在列表里。显然忽略是应该谨慎使用的。

图片 28

提交界面(图3.11)中,鼠标右键单击一个non-versioned文件,弹出菜单中有【Add to ignore list】。以下图为例,它有两个选项,一个是文件本身,另一个是*.pch。前者表示忽略该文件,下次提交时该文件将不再显示在列表里。后者表示忽略所有扩展名为pch的non-versioned文件,下次提交时这些文件将不再显示在列表里。应该谨慎使用后者。

  • 3.9.2 versioned文件
    对于versioned文件,SVN默认将其提交至服务器。如果下次提交的时候,不希望提交某个versioned文件,其方法就是将其移动到提交忽略列表。
    提交界面(图3.11)中,鼠标右键单击一个versioned文件,单击弹出菜单中的【Move to changelist】【ignore-on-commit】菜单项即可将该文件移动到提交忽略列表。
![](https://upload-images.jianshu.io/upload_images/7230921-512ec06f1f355b96.png)
  • 3.10 Update(更新)
    一个软件项目组有多个成员。成员A修改代码并提交之后,成员B如何获得最新版本?方法就是成员B需要更新本地代码。
    Windows资源管理器中,鼠标右键单击检出的项目目录。在弹出菜单中,单击【SVN Update】菜单项。因为要和服务器通讯,因此需要进行用户认证。认证界面如图3.3所示。完成认证后,SVN将更新本地文件为最新版本。
    注意:更新过程中可能会产生冲突,其解决方法请参考"冲突"这一节。

  • 3.11 冲突
    多人维护一套代码,以下情况是难免的:
    1、程序员A和程序员B都修改了文件test.txt,然后都向版本库提交。版本库不知道以哪个版本为准,这会产生文件冲突;
    2、程序员A修改了文件test.txt,向版本库提交时发现文件test.txt已经被程序员B删除了或重命名了或移动到别的目录了,这会产生树冲突。

  • 3.11.1 文件冲突
    文件冲突比较简单,就是多个成员对同一文件进行了修改,版本库不知道该以哪个版本为准。对于文件冲突,这里分两种情况说明。

    • 3.11.1.1 文本文件
      假定项目里有文件1.txt,其内容如下:

图片 29

项目成员A负责维护开发组成员名单,项目成员B负责测试组成员名单。
现在A修改test.txt文件内容并提交:

图片 30

图片 31

项目成员B修改test.txt如下:

图片 32

当项目成员B提交的时候,就会出错。其出错界面如下:

图片 33

上图说明test.txt已经被人修改,需要先更新。成员B按提示要求Update了本机代码,其界面如下。

图片 34

双击冲突,手动解决冲突。

图片 35

请注意Merged这个单词,它表示SVN将版本库最新版本的1.txt和B修改的1.txt合并了。合并的结果如下,其内容正是A、B期望得到的结果。注意这个最新的文件只保留在B的本机上,需要B提交后版本库里的test.txt才能得到更新。

图片 36

也就是说,对于文本文件,SVN有合并能力。它能将项目组成员的改动集中起来,减少了文件冲突的发生。但是SVN的合并不是万能的。假如B对1.txt做了非常大的改动,Update的时候将无法合并。

  • 3.11.1.2 二进制文件
    一个项目里的文件并不都是文本文件,如:Word文档,bmp位图……遇到这类文件,SVN很难通过比较差异来替项目组成员解决冲突了。此时,最有效的方法是使用锁。
    假定项目组成员A要编辑位图1.bmp,正确的操作步骤为:
    1、请在资源管理器中右键单击该文件,单击弹出菜单里的【TortoiseSVN】【Get lock...】菜单项,SVN显示如下。请输入锁定该文件的原因,然后单击"OK"按钮完成文件锁定;

图片 37

图片 38

图片 39

2、如果A未对1.bmp做任何修改,暂时又不想编辑它,请尽快解锁。方法为右键单击该文件,单击弹出菜单里的【TortoiseSVN】【Release lock】菜单项;
3、如果A对1.bmp做了修改并确认无误后,请尽快提交它。方法为右键单击该文件,单击弹出菜单里的【SVN Commit...】菜单项。提交之后,SVN自动解锁该文件。
项目组成员B要编辑1.bmp同样遵循该步骤。因为一个文件不能同时被两个用户锁定,这样就保证了1.bmp不会发生冲突。

  • 3.11.2 树冲突
    发生树冲突的情况比较多,解决起来也比较复杂,应尽量避免。项目组成员需要遵循以下几个原则:
    1、除非必要,否则不要删除、移动、重命名文件;
    2、删除、移动、重命名文件提交时,一定要详细写明删除、移动、重命名了哪些文件,以及为什么这样做。这样其他项目组成员在Update的时候,可以根据这个说明照做一遍,解决树冲突。
  • 3.12 Properties(属性)
    前文提到了提交non-versioned文件时,可以忽略的操作。那么忽略的信息存放在哪儿了呢?答案就是文件夹属性。
    鼠标右键单击检出代码的文件夹,单击弹出菜单的【TortoiseSVN】【Properties】菜单项。SVN显示如下。这里文件夹有一个很有趣的属性svn:ignore,它的值为*.pch,其含义为:提交时将忽略所有扩展名为pch的non-versioned文件。如果你想去除这个功能,就把这条属性编辑一下,或者直接删除即可。
![](https://upload-images.jianshu.io/upload_images/7230921-33ac710f34bd6d0c.png)

<推广> 综合计算器 是一款多功能计算器,可以计算房贷、个税、年终奖、利息、BMI指数以及大小金额的计算器,再也不用下载多个计算器找来找去了。还支持实时更新贷款利率哦~。

打包生成的IPA包路径说明:

../项目所在路径/Archiving/(以AppId命名的文件夹)/(App名+版本号命名的文件夹)/(以打包时间命名的文件夹)

示例:../Archiving//_3_1_29/2018_04_10_11:18:51

分支的创建,删除,合并,切换

图片 40

SVN基础版本架构解决方案

图片 41

多主干多分支并行开发

1.创建分支

右击已经存在的SVN项目->branch/tag,弹出框的to path输入新的分支名称,建议/branch/xxx,点击OK就创建出xxx分支.(建议创建前先svn update)

2.切换分支

右击已经存在的SVN项目->switch,弹出框的to path输入分支名称,上面新建了/branch/xxx分支,如果想要切换/branch/xxx分支,则输入/branch/xxx点击OK.

3.合并分支

右击已经存在的SVN项目->merge->merge a range of revisions->URL to merge from选择需要合并的代码分支,specific range输入框选择show log选择需要合并的版本号区间,然后一路next完成合并,如果有冲突解决完代码冲突,需要svn commit才能完成合并.

4.删除分支

右击已经存在的SVN项目->repo browser,弹出框的左边选择需要删除的分支右击->delete*

一键打包原理

一键打包发布系统其实很简单:开发一款Mac应用,应用启动时读取本机已有的Certificates和Provisioning Profiles信息,再在应用内调用Shell脚本,主要通过脚本来实现Git同步以及打包的相关操作。

  • ###### Shell脚本调用

Objective-C中调用Shell脚本可以使用 NSTask 。通过NSTask,我们可以在应用中调用另一个程序或运行一段脚本并获得其执行状态和最终结果,NSTask最为常用的一个场景是为命令行操作提供图形化的界面。

 //创建一个新的Task NSTask *optTask = [[NSTask alloc] init]; //设置调用路径 optTask.launchPath = shellPath; //设置调用参数 optTask.arguments = @[@"-ls"]; //创建输出Pipe NSPipe *outputPipe = [NSPipe pipe]; [optTask setStandardOutput:outputPipe]; //创建错误输出Pipe NSPipe *errorPipe = [NSPipe pipe]; [optTask setStandardError:errorPipe]; //执行完成Block 通知 optTask.terminationHandler = ^(NSTask *theTask) { [theTask.standardOutput fileHandleForReading].readabilityHandler = nil; [theTask.standardError fileHandleForReading].readabilityHandler = nil; }; //错误输出 [[errorPipe fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) { NSData *data = [file availableData]; NSString *errorMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"errorMsg = %@",errorMsg); }]; //执行结果输出 [[outputPipe fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) { NSData *data = [file availableData]; NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"msg = %@",msg); }]; //开启执行 [optTask launch]; //阻塞直到执行完毕(NSTask默认是异步执行,如果有同步需求,可调用waitUntilExit [optTask waitUntilExit]; [[outputPipe fileHandleForReading] closeFile]; [[errorPipe fileHandleForReading] closeFile];
  • ###### 自动选择签名证书

获取电脑中以iPhone DistributioniPhone Developer命名开头的Certificates:

+ loadCerListBlock:(CerListBlock)listBlock { NSDictionary *options = @{(__bridge id)kSecClass: (__bridge id)kSecClassCertificate, (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll}; CFArrayRef certs = NULL; __unused OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)options, (CFTypeRef *)&certs); NSArray *certificates = CFBridgingRelease; NSMutableArray *tempArray=[NSMutableArray array]; for (int i=0;i<[certificates count];i++) { SecCertificateRef certificate = (__bridge SecCertificateRef)([certificates objectAtIndex:i]); NSString *name = CFBridgingRelease(SecCertificateCopySubjectSummary(certificate)); if ([name hasPrefix:@"iPhone Distribution"]||[name hasPrefix:@"iPhone Developer"]) { [tempArray addObject:name]; } } listBlock(tempArray);}

获取电脑中Provisioning Profiles:

//获取电脑中满足条件的Provisioning Profiles的路径+ (NSArray *)getAllProvisioningProfileList{ NSString *path=[NSString stringWithFormat:@"%@/%@", NSHomeDirectory(), kMobileprovisionDirName]; NSFileManager *fileManager=[NSFileManager defaultManager]; NSArray *provisioningProfiles =[fileManager contentsOfDirectoryAtPath:path error:nil]; provisioningProfiles = [provisioningProfiles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"pathExtension IN %@", @[@"mobileprovision", @"provisionprofile"]]]; return provisioningProfiles;}//自定义 YAProvisioningProfile ,将profile转换为对应实体YAProvisioningProfile *profile = [[YAProvisioningProfile alloc] initWithPath:path];

根据App ID匹配 Profile 描述文件:

//根据appBundleIdentifier匹配 profile 描述文件YAProvisioningProfile *selectProfile = nil;for (YAProvisioningProfile *profile in profileArray) { // 查找与bundleIdentifier相等,描述文件不以 XC: 命名开头,而且是最新的文件 if ([profile.bundleIdentifier isEqualToString:appBundleIdentifier] && ![profile.name hasPrefix:@"XC:"] && profile.newest) { selectProfile = profile; break; }}
  • ###### 读写Xcode工程配置

右键 XXX.xcodeproj 文件,显示包内容,可以看到project.pbxprojproject.pbxproj存储着 Xcode 工程的各项配置参数,我们可以通过脚本来直接读取或修改其配置,执行效果等同于在Xcode的General、Build Settings中的修改。

读取App Bundler Identifier:

# Pbxproj_Path指向 project.pbxproj 文件路径configuration=$(grep -i "PRODUCT_BUNDLE_IDENTIFIER =" ${Pbxproj_Path})Array=($(echo $configuration))# project.pbxproj 中存在多行PRODUCT_BUNDLE_IDENTIFIER信息,从后往前读取Bundle_Identifier=${Array[5]}if [ ! -n $Bundle_Identifier ]then Bundle_Identifier=${Array[3]}fiecho "Bundle_Identifier = ${Bundle_Identifier}"

修改配置信息:修改 project.pbxproj 配置可以通过 sed 命令实现,比如将签名类型指定为Manual

key="CODE_SIGN_STYLE"value="Manual;"# 修改 project.pbxproj 配置,指定签名类型为手动选择sed -i "" "s/$key =.*$/$key = $value/g" $Pbxproj_Path

如果程序成功读取到对应的证书签名,那么打包前需要修改的配置包括:

#修改 PRODUCT_BUNDLE_IDENTIFIERchangeConfiguration "PRODUCT_BUNDLE_IDENTIFIER" "${APP_ID};"#修改 PROVISIONING_PROFILEchangeConfiguration "PROVISIONING_PROFILE" ""${Profile_Name}";"#修改 PROVISIONING_PROFILE_SPECIFIERchangeConfiguration "PROVISIONING_PROFILE_SPECIFIER" "${Profile_Specifier};"#修改 PRODUCT_NAMEchangeConfiguration "PRODUCT_NAME" "${Scheme_Name};"#修改 "CODE_SIGN_IDENTITY[sdk=iphoneos*]"changeConfiguration ""CODE_SIGN_IDENTITY[sdk=iphoneos*]"" '"iPhone Distribution";'#修改 CODE_SIGN_STYLEchangeConfiguration "CODE_SIGN_STYLE" "Manual;"#修改 ProvisioningStylechangeConfiguration "ProvisioningStyle" "Manual;"#修改 DEVELOPMENT_TEAMchangeConfiguration "DEVELOPMENT_TEAM" "${Development_Team};"#修改 DevelopmentTeamchangeConfiguration "DevelopmentTeam" "${Development_Team};"

修改项目版本号:Info.plist文件中CFBundleShortVersionString对应Version号,CFBundleVersion对应Build号,使用脚本可以直接指定版本号

# info.plist文件路径InfoPlist_Path="${Project_Path}/${App_Name}/Info.plist"# 如果是旧的项目,info.plist文件对应的名字为 -Info.plistif [ ! -e "${InfoPlist_Path}" ];then InfoPlist_Path="${Project_Path}/${App_Name}/${App_Name}-Info.plist"fi/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${Version_String}" ${InfoPlist_Path}/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${Version_String}" ${InfoPlist_Path}
  • ###### 打包项目
# 指定打包模式为ReleaseConfiguration="Release"# 判断编译的项目类型是workspace还是projectif [[ ${Is_Workspace} == "YES" ]]; then # 编译前清理工程,>> ${Log_path} 表示将日志输出写入到Log_path文件 xcodebuild clean -configuration "${Configuration}" -alltargets >> ${Log_path} # 自定义签名,指定签名证书 if [[ ${Custom_Sign} == "YES" ]]; then xcodebuild archive -workspace "${Workspace_Path}"  -scheme "${Scheme_Name}"  -archivePath "${Xcarchive_path}"  -configuration "${Configuration}"  PROVISIONING_PROFILE="${Profile_Name}"  CODE_SIGN_IDENTITY="${Sign_Identity}"  >> ${Log_path} else xcodebuild archive -workspace "${Workspace_Path}"  -scheme "${Scheme_Name}"  -archivePath "${Xcarchive_path}"  -configuration "${Configuration}"  >> ${Log_path} fi else # 编译前清理工程 xcodebuild clean -configuration "${Configuration}" -alltargets >> ${Log_path} if [[ ${Custom_Sign} == "YES" ]]; then xcodebuild archive -project "${Xcodeproj_Path}"  -scheme "${Scheme_Name}"  -archivePath "${Xcarchive_path}"  -configuration "${Configuration}"  PROVISIONING_PROFILE="${Profile_Name}"  CODE_SIGN_IDENTITY="${Sign_Identity}" >> ${Log_path} else xcodebuild archive -project "${Xcodeproj_Path}"  -scheme "${Scheme_Name}"  -archivePath "${Xcarchive_path}"  -configuration "${Configuration}"  >> ${Log_path} fifi
  • ###### 导出IPA包
xcodebuild -exportArchive -archivePath "${Xcarchive_path}" -exportPath "${IPA_Archiving_Path}" -exportOptionsPlist "${ExportOptionsPlistPath}" >> ${Log_path}

Xcarchive_path指向上一步打包生成的*.xcarchive文件路径;IPA_Archiving_Path指向导出的*.ipa文件所在路径;ExportOptionsPlistPath对应ExportOptions.plist文件路径,ExportOptions.plist是使用xcodebuild -exportArchive指令导出ipa包时需要指定的配置文件

图片 42ExportOptions.png

  • compileBitcode:不上架App Store,Xcode是否启用Bitcode重新编译,默认为YES。
  • method:归档类型,包括app-store、ad-hoc、package、enterprise、development以及developer-id。
  • provisioningProfiles:打包证书信息,包含的字典信息格式:<key为App ID>:<value为对应的profile文件名>。
  • uploadBitcode:上线App Store是否开启Bitcode,默认为YES。
  • uploadSymbols:上线App Store,是否开启符号序列化,这是与查crash相关的,默认为YES。

关于更多的xcodebuild指令,可以通过xcodebuild -help查看。

  • ###### 其他

其他部分还包括Git代码管理、IPA包上传、无效资源删除等,都可以直接通过NSTask执行脚本命令实现,比如拉取Git代码:git pull origin,当然前提是你本地的Git已经配置为免密操作。如果你是使用SourceTree进行Git管理,而且是http模式,那么可以这样设置免密操作,第3步将远程仓库路径编辑为 http://用户名:密码@仓库地址 这样的方式;当然如果你是SSH模式,那么本身就支持免密码操作了。

图片 43SourceTree.png

上传IPA包。我这里打的是企业包,所以只要将ipa文件上传到指定服务器就能够下载了(想了解更多关于企业包的下载信息,可以参照我的另一篇文章iOS如何部署企业包,以供他人下载)。一开始想着将自动上传也集成到打包程序内,但最后发现这涉及到内网间不同服务器以及账号验证过程,太过复杂暂时将这一部分阉割了……

写在最后

以上便是一键打包系统的功能讲解,想了解更多可以查看源码

CJShellDemo 说明:

ArchiveSource 一键打包程序资源CrashScript 线上crash查找脚本ReleaseDir Xcode打包脚本

欢迎点赞以及GitHub Star

最新补充

根据反馈发现:不同的Xcode版本构建的项目配置文件project.pbxproj会存在差异,另外不同用户电脑上的Provisioning Profiles也存在各种情况(比如团队中不同成员拥有不同名字的有效Profile)。

此时运行一键打包可能会失败!提示 重命名ipa包失败 或者 导出ipa包失败 ,这种情况下请关闭“默认签名”选项,同时打开Xcode在工程文件中手动选择对应打包证书后,再返回Archive.app执行一键打包。

图片 44关闭默认签名.png

应用打包截图

图片 45打包.png

版权声明:本文由大奖888-www.88pt88.com-大奖888官网登录发布于大奖888官网登录,转载请注明出处:一切得从上个版本的打包发布说起,时失败了