iOS使用fastlane自动打包发布

前言

Fastlane是一套使用Ruby写的自动化工具集,目的是为了简化AndroidiOS的部署过程,自动化你的工作流。它可以简化一些乏味、单调、重复的工作,像截图、代码签名以及发布App。这里主要是为了记录一次自己Mac环境下配置fastlane的过程。下图是Fastlane的主要流程和功能

fastlane流程

Fastlane文档

安装最新Xcode命令行工具

1
xcode-select --install

如果你没有安装,会弹出对话框,点击安装即可。


命令行安装工具

如果你已经安装过了,会有如下提示

1
xcode-select: error: command line tools are already installed, use "Software Update" to install updates

Fastlane安装

1
2
3
sudo gem install fastlane -NV
或者
brew cask install fastlane

推荐使用RubyGems安装。

注意??:安装过程中可能会报错You don't have write permissions for the /usr/bin directory.,使用如下代码即可

1
sudo gem install fastlane -n /usr/local/bin

Fastlane安装完成提示

安装完成后可以执行fastlane -v检查是否安装成功.

1
fastlane -v

Fastlane安装检验

初始化Fastlane

  1. cd到你的项目根目录下

    1
     fastlane init

注意??:如果你的工程是用cocoapods的那么可能会提示让你勾选工程的Scheme;步骤就是打开你的xcode-->Manage Schemes-->Scheme-->后面的多选框中进行勾选,然后可以手动删除 fastlane文件夹,重新fastlane init一下。

选择需要处理项

  1. 选择3即可,后续会提示你输入开发者账号和密码,输入账号双重认证的验证码。

  1. 然后输入你的appName即可。

  2. 选择允许fastlane管理ITC上的元数据后,会将ITC上的所有元数据与截图下载到metadatascreenshots文件夹。

允许下载元数据

  1. 后面会出现卡住bundle update步骤中,这是被墙的结果。

bundle update

  1. 只需要关闭终端,打开项目文件夹,找到fastlane init过程中生成的Gemfile文件,并打开,将https://rubygems.org,换成https://gems.ruby-china.com,然后打开终端,cd到当前项目,输入bundle update

修改Gemfile

注意??:过程当中会有很多填写密码或者询问(y/n)

  1. Fastlane初始化完成后,工程根目录下会生成 fastlane文件夹,结构如下:

    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
     fastlane
     ├── Appfile
     ├── Deliverfile
     ├── Fastfile
     ├── metadata
     │   ├── app_icon.jpg
     │   ├── copyright.txt
     │   ├── primary_category.txt
     │   ├── primary_first_sub_category.txt
     │   ├── primary_second_sub_category.txt
     │   ├── review_information
     │   │   ├── demo_password.txt
     │   │   ├── demo_user.txt
     │   │   ├── email_address.txt
     │   │   ├── first_name.txt
     │   │   ├── last_name.txt
     │   │   ├── notes.txt
     │   │   └── phone_number.txt
     │   ├── secondary_category.txt
     │   ├── secondary_first_sub_category.txt
     │   ├── secondary_second_sub_category.txt
     │   ├── trade_representative_contact_information
     │   │   ├── address_line1.txt
     │   │   ├── address_line2.txt
     │   │   ├── address_line3.txt
     │   │   ├── city_name.txt
     │   │   ├── country.txt
     │   │   ├── email_address.txt
     │   │   ├── first_name.txt
     │   │   ├── is_displayed_on_app_store.txt
     │   │   ├── last_name.txt
     │   │   ├── phone_number.txt
     │   │   ├── postal_code.txt
     │   │   ├── state.txt
     │   │   └── trade_name.txt
     │   └── zh-Hans
     │       ├── description.txt
     │       ├── keywords.txt
     │       ├── marketing_url.txt
     │       ├── name.txt
     │       ├── privacy_url.txt
     │       ├── promotional_text.txt
     │       ├── release_notes.txt
     │       ├── subtitle.txt
     │       └── support_url.txt
     └── screenshots
         ├── README.txt
         └── zh-Hans
             ├── 1_iphone6Plus_1.Simulator\ Screen\ Shot\ -\ iPhone\ 8\ Plus\ -\ 2018-06-01\ at\ 17.21.05.png
             ├── 2_iphone6Plus_2.Simulator\ Screen\ Shot\ -\ iPhone\ 8\ Plus\ -\ 2018-06-01\ at\ 17.21.29.png
             ├── 3_iphone6Plus_3.Simulator\ Screen\ Shot\ -\ iPhone\ 8\ Plus\ -\ 2018-06-01\ at\ 17.29.41.png
             ├── 4_iphone6Plus_4.Simulator\ Screen\ Shot\ -\ iPhone\ 8\ Plus\ -\ 2018-06-01\ at\ 17.29.46.png
             └── 5_iphone6Plus_5.Simulator\ Screen\ Shot\ -\ iPhone\ 8\ Plus\ -\ 2018-06-01\ at\ 17.29.51.png
     
     6 directories, 46 files

安装所需插件

Fastlane的插件是一个或者一组action的打包,单独发布在fastlane之外。首次安装插件后会生成一个Pluginfile文件

1
2
3
4
5
6
7
8
9
#查看所有插件
fastlane search_plugins

# 安装方法
fastlane add_plugin [name]
#常用插件
fastlane add_plugin versioning
fastlane add_plugin firim
fastlane add_plugin pgyer

添加插件

插件说明

fastlane add_plugin versioning:用来修改 build 版本号和 version 版本号,fastlane 内嵌的actionincrement_build_number使用的是苹果提供的agvtool, 在更改Build的时候会改变所有target的版本号。如果你在一个工程里有多个target,每次编译,所有的Build都要加1。 有了fastlane-plugin-versioning不仅可以指定target增加Build,当然也可以直接设定Version, 并且可以指定版本号的版本(major/miner/patch)

fastlane-plugin-pgyer:上传到蒲公英分发平台。

fastlane-plugin-firim:上传到firim

注意??:如果你需要发布到蒲公英或者firim必须提前安装插件,否则后面会操作失败。

Fastlane的文件配置

注意??: Fastlane是使用Ruby写的,配置文件修改可以使用VS Code,然后下个Ruby插件这样方便修改。

Ruby插件

Gemfile

类似于cocopodsPodfile文件。

Metadata

元数据文件夹,截图文件夹,一般fastlane init的时候会自动下载下来的。

.env

配置环境变量(在fastlane init进行初始化后并不会自动生成,如果需要可以自己创建)。用于把账号信息、更新描述、工程相关信息、证书文件等单独放在一个配置文件,方便集中更改。

cd到工程目录fastlane文件夹下:

1
touch .env

注意??: .env是一个隐藏文件,我们可以是command+shift+.显示隐藏文件。另外.env可以使用VS Code编辑,但是需要安装dotEnv插件。

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
#APP唯一标识符
  APP_IDENTIFIER = ""

  #苹果开发者账号
  APPLE_ID = ""
 
  #apple developer Team ID
  TEAM_ID = ""

  #itunes connect Team ID
  ITC_TEAM_ID = ""

  #工程名称
  SCHEME_NAME = ""

  #App 元数据及截图存放路径
  METADATA_PATH = "./metadata"
  SCREENSHOTS_PATH = "./screenshots"

  #appicon文件夹路径
  APPICON_PATH = './fastlane/metadata/app_icon.jpg'

  #ipa输出文件夹路径
  OUTPUT_DIRECTORY = "/Users/hfk/Desktop/AlliPA/ipa"

  #App 元数据及截图下载时 直接覆盖 不询问
  DELIER_FORCE_OVERWRITE = false

  #更新描述
  UPDATE_DESCRIPTION = "fastlane自动打包上传测试"

  #自动提交审核
  SUBMIT_FOR_REVIEW = false

  #审核通过后立刻发布
  AUTOMATIC_RELEASE = false

  # 蒲公英 api_key user_key
  PGYER_API_KEY = ''
  PGYER_USER_KEY = ''
  PGYER_INSTALL_TYPE = '2' #1:公开,2:密码安装,3:邀请安装,4:回答问题安装。默认为1公开
  PGYER_INSTALL_PASSWORD = '123456'

  # Firm token
  Firm_API_Token = ''

获取ADC TEAM_ID

使用方法:例如获取工程名称ENV['SCHEME_NAME']

Appfile

存储有关开发者账号相关信息,app_identifier, apple_id, team_id, itc_team_id等一些fastlane需要用到的信息。一般在fastlane init的时候会初始化出来,如果没有就需要自己添加。

1
2
3
4
5
6
7
8
app_identifier ENV['APP_IDENTIFIER'] # The bundle identifier of your app
apple_id ENV['APPLE_ID'] # Your Apple email address

itc_team_id ENV['ITC_TEAM_ID'] # App Store Connect Team ID
team_id ENV['TEAM_ID'] # Developer Portal Team ID

# For more information about the Appfile, see:
#     https://docs.fastlane.tools/advanced/#appfile

Deliverfile

工具的配置文件,metadata 文件夹中的文件比较多且杂,仅一个属性的配置却使用一个单独的.txt文件来存储,官方文档表示可以使用Deliverfile文件来配置且优先级高于matadata.txt文件中的配置。

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# The Deliverfile allows you to store various App Store Connect metadata
# For more information, check out the docs
# https://docs.fastlane.tools/actions/deliver/


############################# 基本信息 ####################################

# bundle identifier
app_identifier ENV['APP_IDENTIFIER']

# Apple ID用户名
username ENV['APPLE_ID']

# 版权声明
# copyright "2020 xxx Inc"

# 支持语言
supportedLanguages = {
  "cmn-Hans" => "zh-Hans"
}

# app 名称
# name({
# 'zh-Hans' => "APPName"
# })

# 副标题
# subtitle(
#   'zh-Hans' => "副标题"
# )

# App价格
#price_tier 0

# 应用程序图标的路径 1024*1024
app_icon './fastlane/metadata/app_icon.jpg'

# 屏幕截图的文件夹的路径
screenshots_path ENV['SCREENSHOTS_PATH']

# 元数据的路径
metadata_path ENV['METADATA_PATH']


###################################  类别配置 ###################################

# 参考网站https://docs.fastlane.tools/actions/upload_to_app_store/#reference
# 设置 App 的类别.这里可以设置一个主要类别,一个次要类别.

# 主要类别
primary_category "MZGenre.Lifestyle"
# 主要类别第一个子类别 无
# primary_first_sub_category
# 主要类别第二个子类别 无
# primary_second_sub_category

# 要设置的次要类别
secondary_category "MZGenre.Utilities"
# 设置的次要第一个子类别 无
# secondary_first_sub_category  
# 设置的次要第二个子类别 无
# secondary_second_sub_category


################################## 关键字\描述等信息 ###################################

# 搜索关键字
# keywords(
#  "zh-Hans" => "FastLane"
# )

# 技术支持网址
# support_url({
#  'zh-Hans' => "http://www.baidu.com"
# })

# 营销网址
#marketing_url({
#  'zh-Hans' => "http://www.baidu.com"
#})

# 隐私政策网址
#privacy_url({
#  'zh-Hans' => "http://www.baidu.com"
#})

# 本地化宣传文本信息介绍
#promotional_text(
#  "zh-Hans" => "本地化宣传文本信息介绍",
#)

# app描述信息
#description({
#  'zh-Hans' => "APP的描述信息,用于APP功能的描述和介绍不能少于10个字符"
#})

# 版本描述
#release_notes({
#  'zh-Hans' => "这是第一个版本哦"
#})


################################## 分级 ########################################

#年龄分级配置
#app_rating_config_path "./fastlane/metadata/itunes_rating_config.json"


################################# 提交审核信息等 #########################################

# 提交审核信息:加密, idfa 等
submission_information({    
    export_compliance_encryption_updated: false,
    export_compliance_uses_encryption: false,
    content_rights_contains_third_party_content: false,
    add_id_info_uses_idfa: false,
    add_id_info_serves_ads: true,
    add_id_info_limits_tracking: true
})

# # 应用审核小组的联系信息 app 审核信息
# app_review_information(
#   first_name: "name",
#   last_name: "name",
#   phone_number: "手机号",
#   email_address: "email",
#   demo_user: "测试账号用户名",
#   demo_password: "测试账号密码",
#   notes: "noting"
# )

# run_precheck_before_submit true

# 在上传新截图之前,先清除所有之前上传的截图
overwrite_screenshots true

# 下载 metadata 及 screenshots 时直接覆盖,不询问
force true

# 不上传截图
skip_screenshots true

# 是否自动提交审核,true表示立马提交审核
submit_for_review ENV['SUBMIT_FOR_REVIEW']

# 审核通过后是否立刻发布,false表示需要手动发布
automatic_release ENV['AUTOMATIC_RELEASE']

Fastfile

核心文件,主要用于命令行调用和处理具体的流程,lane相对于一个方法或者函数。

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:ios)

# 定义全局参数 大写开头为常数,小写、_开头为变量,$开头为全局变量(直接用#访问)
# scheme名
SCHEME_NAME = ENV['SCHEME_NAME']

# workspace名
WORKSPACE_NAME = "#{SCHEME_NAME}.xcworkspace"

# plist文件路径
INFO_PLIST_PATH = "#{SCHEME_NAME}/Info.plist"

# 版本号
$VERSION_NUMBER = ""

# 构建版本号
$BUILD_NUMBER = ""

# ipa导出路径
$OUTPUT_DIRECTORY = ENV['OUTPUT_DIRECTORY']

# ipa安装包路径
$IPA_PATH = ""

# 是否准备完成
$PREPARE_COMPLETED = false

platform :ios do
  before_all do
    # 所有lane执行之前
    # 使用环境变量提供这个密码给fastlane,解决双重认证生成的特殊密码
    ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = "xxx"
    # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
    # 拉取远程最新代码
    # git_pull
    # 执行 pod instasll
    # cocoapods
    # carthage
  end

  desc '打包前的准备工作'
  lane :prepare do |options|
    if !$PREPARE_COMPLETED
      puts "\033[32m====================即将开始打包====================\033[0m\n"
      puts '您好,即将为你自动打包'
    end

    export_method = options[:export_method]
    select_method = '-1'
    # => 如果没有选择打包方式,提示选择打包方式
    if export_method.nil? || export_method.empty?
      puts "请选择打包方式 \033[33m1:打包上传AppStore(默认) 2:打包Development上传蒲公英Firm  3:打包AdHoc上传蒲公英Firm  4:打包Inhouse上传蒲公英Firm  0:结束打包  回车表示使用默认打包\033[0m"
      select_method = STDIN.gets.chomp
      if select_method!='1' && select_method!='2' && select_method!='3' && select_method!='4' && !select_method.empty?
        # supported [:select, :message, :verbose, :error, :password, :input, :success, :important, :command, :user_error!, :command_output, :deprecated, :header, :content_error, :interactive?, :confirm, :crash!, :not_implemented, :shell_error!, :build_failure!, :test_failure!, :abort_with_message!]
          UI.user_error!("您已取消打包 ??")
      end
    end

    if !$PREPARE_COMPLETED
      currentVersion = get_info_plist_value(path: "#{INFO_PLIST_PATH}", key: "CFBundleShortVersionString")
      currentBuild = get_info_plist_value(path: "#{INFO_PLIST_PATH}", key: "CFBundleVersion")
      puts "当前工程的版本号是:\033[33m#{currentVersion}\033[0m 构建版本号是:\033[33m#{currentBuild}\033[0m"
      version = options[:version]
      build = options[:build]
      output_directory = options[:output_directory]
     
      # => 如果没有选择版本号,提示是否需要输入版本号
      if version.nil? || version.empty?
        puts "请输入版本号,回车表示使用当前版本号\033[33m#{currentVersion}\033[0m"
        version = STDIN.gets.chomp
        if version == '' # 回车
          $VERSION_NUMBER = currentVersion
        else
          $VERSION_NUMBER = version
        end
      else
        $VERSION_NUMBER = version
      end
      # => 如果没有选择构建版本号,提示是否需要输入构建版本号
      if build.nil? || build.empty?
        puts "请输入构建版本号,回车表示使用默认自动生成构建版本号"
        build = STDIN.gets.chomp
        if build == '' # 回车
          # $BUILD_NUMBER = AUTO_BUILD_NUMBER
        else
          $BUILD_NUMBER = build
        end
      else
        $BUILD_NUMBER = build
      end
      # => 如果没有选择ipa输出目录,提示是否需要输入打包路径
      if output_directory.nil? || output_directory.empty?
        puts "请指定ipa包输出路径,回车表示使用默认输出路径:\033[33m#$OUTPUT_DIRECTORY\033[0m"
        output_directory = STDIN.gets.chomp
        if output_directory == '' # 回车
        else
          $OUTPUT_DIRECTORY = output_directory
        end
      else
        $OUTPUT_DIRECTORY = output_directory
      end
    end
   
    $PREPARE_COMPLETED = true

    if select_method != '-1' # 已选择
      case select_method
      when '1'
        # 发布到appstore
        release_appstore(options)
      when '2'
        # 打包Development或Adhoc发布到蒲公英Firm
        release_development(options)
      when '3'
        # 打包Adhoc发布到蒲公英Firm
        release_adhoc(options)
      when '4'
        # 打包inhouse发布到蒲公英
        release_enterprise(options)
      end
      next
    end
   
    # => 详细信息
    summary(options)

  end

  desc "信息确认"
  lane :summary do |options|
    puts "\033[32m====================信息确认====================\033[0m\n"
    puts "您设置的包输出路径为:"
    # supported [:select, :message, :verbose, :error, :password, :input, :success, :important, :command, :user_error!, :command_output, :deprecated, :header, :content_error, :interactive?, :confirm, :crash!, :not_implemented, :shell_error!, :build_failure!, :test_failure!, :abort_with_message!]
    UI.important "#$OUTPUT_DIRECTORY"
    puts "您选择的打包方式为:"
    UI.important "#{options[:export_method]}"
    puts "指定的发布版本号为:"
    UI.important "#$VERSION_NUMBER"
    confirm = UI.confirm "确认信息是否正确,输入y继续打包"
    if !confirm
      UI.user_error!("您已取消打包 ??")
    end
    puts "\033[32m====================信息确认====================\033[0m\n"
    puts "3s后开始自动打包..."
    sleep(3)
  end

  desc "更新版本号"
  lane :update_version do
    puts("*************| 更新version #$VERSION_NUMBER |*************")
    increment_version_number_in_plist(
      target: SCHEME_NAME,
      version_number: $VERSION_NUMBER
    )
    puts("*************| 更新build #$BUILD_NUMBER |*************")
    increment_build_number_in_plist(
       target: SCHEME_NAME,
       build_number: $BUILD_NUMBER
    )
  end

  desc "打包发布"
  lane :release do |options|
    prepare(options)
  end

  desc "发布到appstore"
  lane :release_appstore do |options|
    options[:export_method] = "app-store"
    prepare(options)
    build(options)
    deliver_appstore
    end

  desc "发布development"
  lane :release_development do |options|
    options[:export_method] = "development"
    prepare(options)
    build(options)
    deliver_pgyer
    deliver_firm
  end

  desc "发布ad-hoc"
  lane :release_adhoc do |options|
    options[:export_method] = "ad-hoc"
    prepare(options)
    build(options)
    deliver_pgyer
    deliver_firm
  end

  desc "发布企业Inhouse"
  lane :release_enterprise do |options|
    options[:export_method] = "enterprise"
    prepare(options)
    build(options)
    deliver_pgyer
    deliver_firm
  end

  desc "上传到蒲公英"
  lane :deliver_pgyer do |options|
    pgyer(
       api_key: ENV['PGYER_API_KEY'], # 从蒲公英项目详情中获取的apikey
       user_key: ENV['PGYER_USER_KEY'], # 从蒲公英项目详情中获取的 userkey
       #apk: $APK_PATH, #apk包路径
       ipa: $IPA_PATH, #ipa包路径
       install_type: ENV['PGYER_INSTALL_TYPE'], #1:公开,2:密码安装,3:邀请安装,4:回答问题安装。默认为1公开
       password: ENV['PGYER_INSTALL_PASSWORD'], #设置安装密码
       update_description: ENV['UPDATE_DESCRIPTION'] #更新描述
    )
  end

  desc "上传到Firm"
  lane :deliver_firm do |options|
    firim(
       firim_api_token: ENV['Firm_API_Token'], # 从Firm项目详情中获取的apitoken
       ipa: $IPA_PATH, #ipa包路径
       icon: ENV['APPICON_PATH'] #icon
    )
  end
 
  desc "上传到appstore"
  lane :deliver_appstore do |options|
    deliver(
       username: ENV['APPLE_ID'], # 开发者账号
       team_id: ENV['ITC_TEAM_ID'], # ITC Team ID
       dev_portal_team_id: ENV['TEAM_ID'], # ADC Team ID
       app_identifier: ENV['APP_IDENTIFIER'], # bundle ID
       ipa: $IPA_PATH, # ipa包路径
       app_version: $VERSION_NUMBER, # 更新版本号
       release_notes: {
         'zh-Hans' => "这是第一个版本哦"
       },
       force: true, # 设置true,会跳过预览页面,直接上架
       skip_screenshots: true, # 不上传截图
       skip_metadata: true, # 不上传元数据
    )
  end

  desc "打包"
  lane :build do |options|
    # gym用来编译ipa
    # 编译时间
    build_time = Time.now.strftime("%Y-%m-%d %H-%M-%S")
    # 自动生成的build版本号
    auto_build_number = Time.now.strftime("%Y%m%d%H%M%S")
    if $BUILD_NUMBER.empty?
      $BUILD_NUMBER = auto_build_number
    end

    # 更新版本号
    update_version

    # 获取打包方式
    export_method = options[:export_method]
    # 配置项
    configuration = 'Release'
    # pp文件
    provisioningProfiles = ENV['PP_APPSTORE']
    # 输出目录
    outputDir = ''
    # 输出文件名
    outputName = "#{SCHEME_NAME}_#$VERSION_NUMBER_#$BUILD_NUMBER_#{export_method}.ipa"
    case export_method
    when 'development'
      configuration = 'Debug'
      outputDir = "#$OUTPUT_DIRECTORY/Development/#{SCHEME_NAME}-#{build_time}"
    when 'app-store'
      outputDir = "#$OUTPUT_DIRECTORY/Appstore/#{SCHEME_NAME}-#{build_time}"
    when 'ad-hoc'
       provisioningProfiles = ENV['PP_ADHOC']
       outputDir = "#$OUTPUT_DIRECTORY/ADHOC/#{SCHEME_NAME}-#{build_time}"
    when 'enterprise'
       provisioningProfiles = ENV['PP_ENTERPRISE']
       outputDir = "#$OUTPUT_DIRECTORY/ADHOC/#{SCHEME_NAME}-#{build_time}"
    end
   
    $IPA_PATH = gym(
       clean: 'true', # 在打包前是否先执行clean。
       scheme: "#{SCHEME_NAME}", # 指定项目的scheme名称
       workspace: "#{WORKSPACE_NAME}", # 指定.xcworkspace文件的路径。
       configuration: "#{configuration}", # 指定打包时的配置项,默认为Release
       output_name: "#{outputName}", # 指定生成的.ipa文件的名称,应包含文件扩展名。
       output_directory: "#{outputDir}", # 指定.ipa文件的输出目录
       include_symbols: 'true', # 是否导出符号表
       # include_bitcode: 'false',  # 是否使用bitcode打包
       export_xcargs: "-allowProvisioningUpdates", #访问钥匙串
       silent: true,  # 是否隐藏打包时不需要的信息。
       buildlog_path: "#{outputDir}", # 指定编译日志文件的输出目录
       export_options: {
        method: "#{export_method}", # 指定导出.ipa时使用的方法,可用选项:app-store,ad-hoc,enterprise,development
        thinning: "<none>",  # 是否瘦身
       }
    )
  end

  after_all do |lane|
    # 在macOS 通知栏发送通知
    notification(subtitle: "Successfully", message: "Successfully deployed new App Update")
    #slack(
    #   message: "Successfully deployed new App Update."
    #)
  end

  error do |lane, exception|
     #slack(
     #  message: exception.message,
     #  success: false
     #)
  end

end

Fastlane发布

cd到工程目录下,调用fastlane release,之后根据提示选择需要的过程即可完成发布。

注意??: 如果出现如下错误,就去工程中Edit Scheme --> 勾中Shared

1
Couldn't find specified scheme 'YourScheme'. Please make sure that the scheme is shared, see ...

Edit Scheme

两步验证问题

现在大部分Apple Id都开启了两步验证,当我们提交TestFilght或者AppStore的时候会提示我们输入6位code,这样就违背全自动发布的初衷了。

1
2
3
4
Two Factor Authentication for account '[email protected]' is enabled
If you're running this in a non-interactive session (e.g. server or CI)
check out https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification
Please enter the 6 digit code:

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
步骤:
1.访问 https://appleid.apple.com/account/manage
2.生成一个 APP-SPECIFIC PASSWORDS,保留生成的特殊密码,记作 A
3.使用环境变量提供这个密码给fastlane:  FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
4.执行 fastlane spaceauth -u [email protected],生成 session cookie,记作 B。
5.通过环境变量 FASTLANE_SESSION 提供 session cookies。

配置:
方式1:
前往文件 ~/.bash_profile 中,新增并配置配置 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 和 FASTLANE_SESSION
例:(A和B就是上面生成的)
export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD = A
export FASTLANE_SESSION = B
/*----------------------------------------------------*/
方式2:
ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = "xxxxxx"
这个和环境变量配置是一个意思,但是涉及密码写在配置文件里面

注意??: .bash_profile文件是隐藏文件,可以使用command+shift+.控制显示

配置bash_profile

生命周期

执行顺序 方法名 说明
1 before_all 在执行lane之前操作,只执行一次,比如使用cocopods更新 pod 库
2 before_each 每次执行lane之前都会执行一次
3 lane 用户自定义的任务流程,例如打包,上传等
4 after_each 每次执行lane之后都会执行一次
5 after_all 在执行所有lane成功结束之后执行一次,例发送邮件,通知
6 error 在执行上述情况任意环境报错都会中止并执行一次

fastlane常用工具

工具 描述
scan 自动运行测试工具,并且可以生成漂亮的HTML报告
cert 自动创建和管理iOS签名证书Certificates
sign 创建、更新、下载、修复Provisioning Profiles的工具
pem 自动生成、更新推送配置文件
match 一个新的自动创建和管理iOS签名证书和Provisioning Profiles工具(一般团队使用)
snapshot UI test功能实现自动化截图
frameit 给截屏套上一层外边框
gym 编译打包生成ipa文件,又名build_ios_appbuild_app
produce 如果你的产品还没在iTC或者ADC建立,produce可以自动帮你完成这些工作
deliver 自动上传截图,APP的元数据,二进制ipa文件到iTC
notification Mac上面显示通知