Unity自动化发版解决方案

目标

  • 自动更新Unity工程
  • 打包应用apk/ipa
  • 上传至服务器,便于分发测试

方案

准备使用Jenkins+Python+Fir的组合来解决IOS即成发版的问题

  • Jenkins:用来自动化部署的框架,所有流程需要在其内配置完成。
  • Python:用Python脚本来处理命令行相关,需要一些初步的python语法基础。
  • Fir.im:一个不错的移动应用测试发布平台。

执行

配置环境

因为开发环境是mac,为了方便安装,这里先安装一下mac下的软件管理工具,Homebrew,后续很多安装使用它可以更加便利。

  • 安装Homebrew:

    Homebrew来安装,这是一个非常便捷的软件管理工具,类似于Ubuntu上的apt-get。如果已安装,或不需要,请跳过。

    • 安装Homebrew,复制这段命令到终端执行:/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    • 使用方法:终端中使用命令brew install packageName
  • 安装Jenkins:

    Jenkins的安装方法有很多,详细参见官网。下面是使用Homebrew安装

    • 安装命令:brew install jenkins
    • 启动服务:brew services start jenkins
    • 终止服务:brew services stop jenkins
    • 重启服务:brew services restart jenkins

      注意:默认homebrew 安装的jenkins默认配置监听是127.0.0.1,因此不支持局域网其他机器访问,因此需要修改jenkins的配置表,将其修改为0.0.0.0,端口后默认8080,也可以修改,不必要。这个配置文件需要找到brew的安装目录:/usr/local/Cellar/ 这里面有jenkins文件家,根据版本进入路径,找到一个homebrew.mxcl.jenkins.plist文件,用编辑器打开并编辑httpListenAddress保存文件,并重启服务。

  • 安装Python:

    mac系统自带python2.7,因为脚本需要python3,因此需要单独安装,你可以去官网下载安装,同样也可以使用homebrew安装:brew install python

  • 配置fir工具

    -注册fir账号,获得token,这个token将在上传包的时候会用到。
    fir有一套自己的命令行工具,同时也开发了一个jenkins插件。两种方案在这里都是可行的。

配置jenkins项目

  • 启动Jenkins:适用上文提到的命令brew services start jenkins
  • 解锁Jenkins:打开浏览器:http://localhost:8080,第一次启动jenkins,会显示解锁页面,提示去打开文件:/Users/用户名/.jenkins/secrets/initialAdminPassword来找到初始密码。打开finder,适用快捷键:cmd+shit+g,复制改地址,代开文件获取密码。
  • 等待初始化完毕
  • 新建项目:自由风格
  • 配置svn or git:适用jenkins svn 插件,设置项目库地址和账号
  • 配置构建脚本:这里使用python脚本,详见下文,脚本主要功能:
    • 编译Unity工程(导出IOS工程)
    • Archive
    • 导出Ipa
    • 【可选】适用fir-cli上传包
  • 【可选】配置fir插件,上传包

打包脚本

这里给出的是ios发版脚本,因为android比较简单,直接生成apk,可以看作是ios脚本的缩减版(省去archiv和export ipa,只调用下Unity命令行即可)。

另外脚本中调用的Unity发版脚本就略去了,因为看unity文档写一个很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/local/bin/python3

import os
import sys
import shlex
import subprocess

#配置项
BUILD_TYPE = "模式:Debug或者Release"
PROJECT_PATH = "项目路径"
OUTPUT_PATH ="/builds"
PACKAGE_NAME ="项目名称"
FIR_APITOKEN = "fir.im的token"
CODE_SIGN_IDENTITY_DEV = "证书文件名称"
PROVISION_PROFILE_DEV = "Provsionprofile文件uuid"

# jenkins环境下,配置相对路径
if "WORKSPACE" in os.environ:
PROJECT_PATH = os.environ["WORKSPACE"] + PROJECT_PATH
OUTPUT_PATH = os.environ["WORKSPACE"] + OUTPUT_PATH


# xcode project output path
XCODE_PROJECT_PATH = OUTPUT_PATH + "/" + PACKAGE_NAME + "_project"
XCODE_ARCHIVE_PATH = OUTPUT_PATH + "/" + PACKAGE_NAME + "_archive"

# unity命令行模版
BUILD_COMMAND_TEMPLATE = "/Applications/Unity/Unity.app/Contents/MacOS/Unity" \
" -quit -batchmode" \
" -logFile /dev/stdout -projectPath {0}" \
" -executeMethod 这里是你自己实现的UnityClass.发版方法{1:Debug/Release} -targetLocation {2}" \
" -buildTarget ios"

#xcode 相关命令行模版
XCODE_OPTIONS_PLIST_FILE_PATH = PROJECT_PATH+"/"+"BuildSetting/exportPlist.plist"
XCODE_BUILD_COMMAND_TEMPLATE = "xcodebuild -project {0} -scheme {1} build"
XCODE_ARCHIVE_COMMAND_TEMPLATE = "xcodebuild -project {0} -scheme {1} archive \
-archivePath {2} \
CODE_SIGN_IDENTITY='{3}' \
PROVISIONING_PROFILE={4}"

XCODE_EXPORT_COMMAND_TEMPLATE = "xcodebuild -exportArchive \
-archivePath {0} \
-exportPath {1} \
-exportOptionsPlist {2} \
DWARF_DSYM_FOLDER_PATH={3}"

#fir.im命令行
FIR_CLI_LOGIN = "fir login {0}"
FIR_CLI_PUBLISH = "fir publish {0}"


def buildIOS():
#1 清理目录
if not os.path.exists(XCODE_PROJECT_PATH):
os.makedirs(XCODE_PROJECT_PATH)
print("UNITY_BUILD_OUTPUT_PATH is created at \"{0}\"".format(XCODE_PROJECT_PATH))
else:
remove_build_folder_cmd = "rm -rf {0}".format(XCODE_PROJECT_PATH)
os.system(remove_build_folder_cmd)
print("Clean up the unity build output folder at \"{0}\"".format(XCODE_PROJECT_PATH))
os.makedirs(XCODE_PROJECT_PATH)

if not os.path.exists(XCODE_ARCHIVE_PATH):
os.makedirs(XCODE_ARCHIVE_PATH)
print("XCODE_ARCHIVE_PATH is created at \"{0}\"".format(XCODE_ARCHIVE_PATH))
else:
remove_xcode_archive_cmd = "rm -rf {0}".format(XCODE_ARCHIVE_PATH)
os.system(remove_xcode_archive_cmd)
print("Clean up the xcode archive folder at \"{0}\"".format(XCODE_ARCHIVE_PATH))
os.makedirs(XCODE_ARCHIVE_PATH)

#导出xcode工程
build_cmd = shlex.split(BUILD_COMMAND_TEMPLATE.format(
PROJECT_PATH,
BUILD_TYPE ,
XCODE_PROJECT_PATH))
subprocess.call(build_cmd)

# 编译xcode
# Archiving
archive_file_name = PACKAGE_NAME + ".xcarchive"
project_file_name = "Unity-iPhone.xcodeproj"
xcode_scheme_name = "Unity-iPhone"
full_project_file_path = os.path.join(XCODE_PROJECT_PATH, project_file_name)
full_archive_file_path = os.path.join(XCODE_ARCHIVE_PATH, archive_file_name)
archive_xcode_cmd = shlex.split(XCODE_ARCHIVE_COMMAND_TEMPLATE.format(
full_project_file_path,
xcode_scheme_name,
full_archive_file_path,
CODE_SIGN_IDENTITY_DEV,
PROVISION_PROFILE_DEV))
subprocess.call(archive_xcode_cmd)

# 导出ipa
full_export_folder_path = os.path.join(XCODE_ARCHIVE_PATH, PACKAGE_NAME)

full_option_plist_file_path = XCODE_OPTIONS_PLIST_FILE_PATH
export_xcode_cmd = shlex.split(XCODE_EXPORT_COMMAND_TEMPLATE.format(
full_archive_file_path,
full_export_folder_path,
full_option_plist_file_path,
XCODE_ARCHIVE_PATH))
subprocess.call(export_xcode_cmd)

# 上传 可选
fir_login_cmd = shlex.split(FIR_CLI_LOGIN.format(FIR_APITOKEN))
subprocess.call(fir_login_cmd)

fir_publish_cmd = shlex.split(FIR_CLI_PUBLISH.format(XCODE_IPA_PATH))
subprocess.call(fir_publish_cmd)


if __name__ == "__main__":
buildIOS()

注意事项,坑

发版机器的Unity也需要专门配置一下:

  • Unity preference中的ios导出中auto manage code sign的选项钩钓,否则到处的xcode工程的设置就会有问题,导致命令行报错。
  • 可以添加一个local的cache server,方便且平台发版

关于xcodebuild设置,这里可以看我的另一篇文章IOS自动化构建工具:xcodebuid

运行和测试

运行脚本,最后会生成短链,可供下载安装

测试建议:

- 先测试jenkins流程是否通常
- 测试python脚本是否可以独立运行通过
- 测试fir上传功能是否有效
- 联合测试