需求

DONE

  • 生成可以在 iOS 上直接安装的 ipa 包。
  • Debug Release 版本选择。
  • 有默认配置,可以通过命令行修改预设参数。

TODO

  • Android 版本的打包。
  • 支持上传到 Fir.im、蒲公英 等等。
  • 记录环境信息,包括:Unity 版本、Xcode 版本、Ruby 版本。
  • 记录所有程序输出的日志,方便查找错误。
  • 可以使用指定版本构建。
  • 计时,记录每一步消耗的时间。

过程

语言

这种打包脚本使用操作系统自带的脚本语言最方便。第一选择是 Python,但是之前写过类似的,这次就想换 Ruby 尝试一下,最终决定使用 Ruby 来编写这个工具。 Ruby 本身用起来非常简单,OS X 自带的 Ruby 版本 2.0.0,已经非常成熟了。

提示:使用 ls 这种语法调用时请不要使用中文路径,中文在路径中会消失,而不管 Ruby 源文件是否是 utf-8 编码。

提示:在使用字符串时需要双引号使用,以方便在其中嵌入变量,同时应对所有命令中变化的值使用 \\” 引用,防止为空时出错。此处不应使用 \\‘,在 shell 中,\\’ 并不会对字符串中的内容进行转义!

Unity

从命令行调用参考官方文档 Unity - Manual: Command line arguments 即可。 Unity 内部需要辅助静态函数来打包,这里使用到了之前写的一个库:networm/Infinity.BuildInUnity3d

BuildInUnity3d 库的作用就是套一层封装,同时配合 Build 库实现构建 Debug 与 Release 版本。 因为 Unity 不支持传递自定义参数,即使传递了自定义参数,使用 System.Environment.GetCommandLineArgs() 也无法获取,最终变通方法是使用不同的构建函数:BuildPlayerDevelop()BuildPlayerRelease()

提示:Unity 的日志输出可以重定向到标准输出(即终端中),只要在调用时指定参数 -logFile $stdout

Xcode

xcode 构建这里麻烦一些,涉及到的步骤较多:

  • 安装 mobileprovision
  • 创建并解锁 keychain
  • 导入 p12 开发者私钥与证书
  • 编译、打包

codesign

有关如何禁止 codesign 签名时弹窗,网上有很多方案,但是只有代码,并没有解释得很清楚。

codesign 在签名时会使用两样东西:keychain 与 private key。其中 private key 存储在 keychain 中。 codesign 在签名时会寻找系统指定的 keychain 搜索列表。不要被 codesign 的 --keychain filename 选项蒙蔽,这个选项要求 filename 必须在 keychain 搜索列表中。 用户有一个默认的 login.keychain,密码是用户自己的密码,如果导入 p12 时导入到 login.keychain 中就必须要求要么用户输入密码,要么将密码写死在脚本中,这样很危险,而且无法做到开包即用。

这里使用的一个变通方法:

  1. 首先创建一个 keychain 并解锁。
  2. 将 p12 导入到新建的 keychain 中并通过 -T /usr/bin/codesign 参数指定 codesign 使用的私钥。
  3. 再通过 security list-keychains -s 命令将创建的 keychain 设置为用户的 keychain 搜索列表。
  4. 直接使用 codesign 或通过 xcodebuild 间接使用 codesign 签名。
  5. 将用户的 keychain 搜索列表设置为默认的 login.keychain。
  6. 删除创建的 keychain。

第 5 步强烈建议做,如果不做的话,用户打开 钥匙链 应用时,只能看到刚创建的“keychain”,而看不到默认的“登录”。

提示:创建 keychain 后建议解锁,否则在 300 秒超时后 codesign 在签名时会要求解锁。

提示:import p12 时一定要指定 -T /usr/bin/codesign 否则 codesign 在执行时会提示对话框询问是否允许使用私钥。

mobileprovision

mobileprovision 安装很简单,将其拷贝到 ~/Library/MobileDevice/Provisioning Profiles 目录中,并将其改名为 $UUID.mobileprovision

其中的 UUID 可以通过

grep "UUID" ${IOS_PROVISIONING_FILE_PATH} -A 1 --binary-files=text 2>/dev/null |grep string|sed -e 's/^[[:blank:]]<string>//' -e 's/<\/string>//'

命令获取。 但是在 Ruby 中直接调用并不会获取正确的结果,所以此处使用 nokogiri xml 库来读取 UUID。

iOS 布署方式

  • app-store 是发布到 App Store 的方式(注意:TestFlight 使用的是 app-store,而不是 ad-hoc)。
  • ad-hoc 是发布给其他人使用的方式。
  • development 是开发者布署到自己的测试设备上的方式。

提示:提交到 iTunes Connect 中的应用的 Build 版本号每一个都要不一样,否则会提示重复,所以在每次构建前都要设置 –build-version 参数。 因为这个 Build 版本号存放在 Xcode 项目的 Info.plist 中,可以使用 PlistBuddy 修改。

提示:在构建 app-store 与 ad-hoc 时要求的 exportOptionsPlist 中有一个 team ID,可以从导入的 P12 中直接提取。

Source Code

GitHub: networm/Infinity.Build

GitHub: networm/Infinity.BuildInUnity3d

LICENSE

MIT LICENSE

参考资料